├── .gitmodules ├── .gitignore ├── lokilib.c ├── test ├── test_socket.c ├── test_listener.c ├── test_callback.c └── loki_test.c ├── README.md ├── loki_services.h ├── lk_buffer.h ├── service_listener.c ├── service_timer.c ├── service_loader.c ├── service_log.c ├── service_socket.c └── loki.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "znet"] 2 | path = znet 3 | url = https://github.com/starwing/znet.git 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.dll 3 | *.exp 4 | *.ilk 5 | *.lib 6 | *.obj 7 | *.pdb 8 | *.lastcodeanalysissucceeded 9 | *.o 10 | *.so 11 | *.un~ 12 | *_test 13 | loki_test 14 | logs/ 15 | *.dSYM/ 16 | .vscode/ 17 | tags 18 | -------------------------------------------------------------------------------- /lokilib.c: -------------------------------------------------------------------------------- 1 | #define LOKI_IMPLEMENTATION 2 | #include "loki_services.h" 3 | 4 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 5 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 6 | * cc: flags+='-Wextra -O3' input='service_*.c lokilib.c' */ 7 | 8 | -------------------------------------------------------------------------------- /test/test_socket.c: -------------------------------------------------------------------------------- 1 | #define LK_DEBUG_MEM 2 | #define LOKI_IMPLEMENTATION 3 | #include "../loki_services.h" 4 | 5 | #include 6 | 7 | static void on_tcp(lk_State *S, void *ud, unsigned err, lk_Accept *accept, lk_Tcp *tcp) { 8 | lk_log(S, "connected, send something..."); 9 | lk_send(tcp, "Hello World!", 12); 10 | lk_deltcp(tcp); 11 | lk_delaccept(accept); 12 | } 13 | 14 | static size_t on_header(lk_State *S, void *ud, lk_Tcp *tcp, const char *s, size_t len) { 15 | if (s) { 16 | lk_log(S, "recieved: [%.*s](%d), closing", (int)len, s, (int)len); 17 | lk_close(S); 18 | } 19 | return len; 20 | } 21 | 22 | int main(void) { 23 | lk_State *S = lk_newstate(NULL, NULL, NULL); 24 | lk_Accept *accept; 25 | lk_Service *svr; 26 | lk_launch(S, "log", loki_service_log, NULL); 27 | svr = lk_launch(S, "socket", loki_service_socket, NULL); 28 | lk_setonheader(svr, on_header, NULL); 29 | accept = lk_newaccept(svr, on_tcp, NULL); 30 | lk_listen(accept, "127.0.0.1", 12345); 31 | lk_connect(svr, "127.0.0.1", 12345, NULL, NULL); 32 | lk_start(S, 0); 33 | lk_waitclose(S); 34 | lk_close(S); 35 | return 0; 36 | } 37 | 38 | /* unixcc: flags+='-ggdb' input+='service_*.c' libs+='-pthread -ldl' */ 39 | /* win32cc: output='test_socket.exe' input+='service_*.c' libs+='-lws2_32' */ 40 | 41 | -------------------------------------------------------------------------------- /test/test_listener.c: -------------------------------------------------------------------------------- 1 | #define LOKI_IMPLEMENTATION 2 | #include "../loki_services.h" 3 | 4 | 5 | static int on_echo(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 6 | lk_log(S, "receive message: %s", sig->data); 7 | sig->isack = 1; 8 | lk_emit(sender, sig); 9 | return LK_OK; 10 | } 11 | 12 | static int on_echo_return(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 13 | lk_log(S, "from %s return: %s", sender, sig->data); 14 | lk_close(S); 15 | return LK_OK; 16 | } 17 | 18 | static int on_echo_listener(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 19 | lk_log(S, "from listener#%d (slot: %s): %s", 20 | (int)(ptrdiff_t)sig->source->ud, sender, sig->data); 21 | return LK_OK; 22 | } 23 | 24 | int main(void) { 25 | lk_State *S = lk_newstate(NULL, NULL, NULL); 26 | lk_Slot *echo = lk_newslot(S, "echo", on_echo, NULL); 27 | lk_Service *listener = lk_launch(S, "listener", loki_service_listener, NULL); 28 | lk_launch(S, "log", loki_service_log, NULL); 29 | lk_setcallback(S, on_echo_return, NULL); 30 | lk_addlistener(listener, echo, on_echo_listener, (void*)(ptrdiff_t)1); 31 | lk_addlistener(listener, echo, on_echo_listener, (void*)(ptrdiff_t)2); 32 | lk_addlistener(listener, echo, on_echo_listener, (void*)(ptrdiff_t)3); 33 | lk_addlistener(listener, echo, on_echo_listener, (void*)(ptrdiff_t)4); 34 | lk_addlistener(listener, echo, on_echo_listener, (void*)(ptrdiff_t)5); 35 | lk_emitstring(echo, 0, "Hello slot!"); 36 | lk_start(S, 0); 37 | lk_waitclose(S); 38 | lk_close(S); 39 | return 0; 40 | } 41 | 42 | /* cc: flags+='-Wextra -ggdb -O0' input+='service_log.c service_listener.c' 43 | * unixcc: libs+='-pthread -ldl' 44 | * win32cc: libs+='-lws2_32' */ 45 | 46 | -------------------------------------------------------------------------------- /test/test_callback.c: -------------------------------------------------------------------------------- 1 | #define LOKI_IMPLEMENTATION 2 | #include "../loki_services.h" 3 | 4 | static int on_echo(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 5 | if (sig->source) 6 | lk_log(S, "echo: %d: %s", (int)(ptrdiff_t)sig->source->ud, sig->data); 7 | else 8 | lk_log(S, "echo: %s", sig->data); 9 | sig->isack = 1; 10 | lk_emit((lk_Slot*)lk_service(sender), sig); 11 | return LK_OK; 12 | } 13 | 14 | static int on_poll(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 15 | int i = 0; 16 | for (;;) { 17 | lk_log(S, "poll: in loop %d", i++); 18 | if (lk_wait(S, sig, -1) != LK_OK) 19 | break; 20 | if (sig->source) 21 | lk_log(S, "poll: %d: %s", (int)(ptrdiff_t)sig->source->ud, sig->data); 22 | else 23 | lk_log(S, "poll: %s", sig->data); 24 | sig->isack = 1; 25 | lk_emit((lk_Slot*)lk_service(sender), sig); 26 | } 27 | lk_log(S, "poll: exiting..."); 28 | return LK_OK; 29 | } 30 | 31 | static int on_echo_return(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 32 | int count = (int)(ptrdiff_t)sig->source->ud; 33 | (void)sender; 34 | lk_log(S, "echo callback: %d: %s", count, sig->data); 35 | if (count != 0) { 36 | lk_Slot *echo = lk_slot(S, "echo"); 37 | lk_Slot *poll = lk_slot(S, "poll"); 38 | /*lk_emitstring(echo, 0, "Hello");*/ 39 | sig->source->ud = (void*)(ptrdiff_t)(count - 1); 40 | sig->isack = 0; 41 | if (echo) lk_emit(echo, sig); 42 | if (poll) lk_emit(poll, sig); 43 | } 44 | else { 45 | lk_log(S, "echo exiting..."); 46 | lk_close(S); 47 | } 48 | return LK_OK; 49 | } 50 | 51 | int main(void) 52 | { 53 | lk_State *S = lk_newstate(NULL, NULL, NULL); 54 | lk_Slot *echo = lk_newslot(S, "echo", on_echo, NULL); 55 | lk_Slot *poll = lk_newpoll(S, "poll", on_poll, NULL); 56 | lk_setcallback(S, on_echo_return, (void*)5); 57 | lk_emitstring(echo, 0, "Hello to slot"); 58 | lk_setcallback(S, on_echo_return, (void*)5); 59 | lk_emitstring(poll, 0, "Hello to poll"); 60 | lk_launch(S, "log", loki_service_log, NULL); 61 | lk_log(S, "======================="); 62 | lk_log(S, "test_callback start ..."); 63 | lk_log(S, "======================="); 64 | lk_start(S, 0); 65 | lk_waitclose(S); 66 | lk_close(S); 67 | return 0; 68 | } 69 | 70 | /* cc: flags+='-Wextra -ggdb -O0' input+='service_*.c' 71 | * unixcc: libs+='-pthread -ldl' 72 | * win32cc: libs+='-lws2_32' */ 73 | 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `loki` - one-header online game framework 2 | ---------------------------------------- 3 | 4 | `loki` is a online game framework, inspired by cloudwu's 5 | [skynet](https://github.com/cloudwu/skynet). 6 | 7 | To start, see the header file 8 | [loki.h](https://github.com/starwing/`loki`/blob/master/`loki`.h). 9 | `loki` now doesn't have documentations for now, but I will finish that after 10 | complete the built-in service. 11 | 12 | `loki` is not only used in online game server, but more. It's a *multi-thread* 13 | service *signal/slot* system. Every service register it's own slots, and 14 | send/receive message to offer service. 15 | 16 | `loki` only have one header as it's core. To define `loki` functions (only 17 | allow in only **one** C file), define `LOKI_IMPLEMENTATION` before include 18 | `loki.h`. `loki` also has several *built-in* service, that will defined as 19 | `lsvr_*.c` files, each file can singly build as a DLL/so file, or build 20 | together with `lsvr_init.c` file. 21 | 22 | In `loki`, you must create a `lk_State` to contain all information `loki` 23 | used, `loki` doesn't use global variables. A `lk_State` is also a 24 | `lk_Service`, and a `lk_Service` is also a `lk_Slot`. To get the name of a 25 | slot, just cast it to `const char*`. You can emit signals to slot by 26 | `lk_emit()`, and process them in a function called `lk_SlotHandler`. 27 | 28 | A service must have a function named `lk_ServiceHandler` to register slots. 29 | All slots it registered are prefixed the name of the service. Service's name 30 | limited to 31 characters, and the name of slot are limited to 63 character. 31 | 32 | e.g. If you have a service 'foo' and it create a slot 'bar', then the slot is 33 | named 'foo.bar'. 34 | 35 | You can find a slot/service by it's name, and send a message to that.After 36 | slot process the message, it may return a response message to you by default. 37 | This response will directly send to your service. (remember your service is 38 | also a slot?) 39 | 40 | loki is that simple library offer these message passing service. It can used 41 | on Windows or POSIX systems. It can load DLL/so to get new services. If you 42 | have a `foo.dll` on Windows, you should only define a entry function named 43 | `loki_service_foo`, then loki can load it and offer new service. 44 | 45 | loki uses multi-thread, but every single service's handler are running in 46 | order, just like other single thread programs. You needn't use locks in a 47 | single service, you can use message to communicate with other service to avoid 48 | the use of locks. 49 | 50 | loki offers such built-in service: 51 | - timer: register timer to callback after several times. 52 | - task: to run a long-time task in other thread, or run a poll thread 53 | backward. 54 | - socket: offer network ability. Implemented my other library 55 | [znet](https://github.com/starwing/znet). 56 | - harbor: multi-process `loki` server support. 57 | - logger: logger whatever you want. 58 | - monitor: to get internal informations about `loki` services. 59 | 60 | 61 | PLAN 62 | ---- 63 | 64 | Loki's core library is completed. 65 | The built-in services are work-in-progress. 66 | 67 | 68 | License 69 | ------- 70 | 71 | Loki use MIT license. 72 | 73 | -------------------------------------------------------------------------------- /test/loki_test.c: -------------------------------------------------------------------------------- 1 | #undef LOKI_IMPLEMENTATION 2 | #define LOKI_IMPLEMENTATION 3 | #include "../loki_services.h" 4 | 5 | static size_t totalmem; 6 | static lk_Lock memlock; 7 | 8 | static void *debug_allocf(void *ud, void *ptr, size_t size, size_t osize) { 9 | void *newptr = NULL; 10 | (void)ud; 11 | if (size == 0) free(ptr); 12 | else newptr = realloc(ptr, size); 13 | lk_lock(memlock); 14 | totalmem += size; 15 | totalmem -= osize; 16 | lk_unlock(memlock); 17 | if (osize == 0) 18 | printf(" malloc: %p:%d (%d)\n", newptr, (int)size, (int)totalmem); 19 | else if (size == 0) 20 | printf(" free: %p:%d (%d)\n", ptr, (int)osize, (int)totalmem); 21 | else 22 | printf(" realloc: %p:%d -> %p:%d (%d)\n", 23 | ptr, (int)osize, newptr, (int)size, (int)totalmem); 24 | return newptr; 25 | } 26 | 27 | static int echo(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 28 | lk_log(S, "msg: %s", (char*)sig->data); 29 | lk_log(S, "T[]" lk_loc("get msg: [%s]"), (char*)sig->data); 30 | sig->isack = 1; 31 | lk_emit((lk_Slot*)lk_service(sender), sig); 32 | return LK_OK; 33 | } 34 | 35 | static lk_Time repeater(lk_State *S, void *ud, lk_Timer *timer, lk_Time elapsed) { 36 | lk_Slot *echo = lk_slot(S, "echo.echo"); 37 | int *pi = (int*)ud; 38 | (void)timer; 39 | if ((*pi)++ == 10) { 40 | lk_free(S, pi, sizeof(int)); 41 | lk_close(S); 42 | return 0; 43 | } 44 | lk_log(S, "V[] timer: %d: %u", *pi, elapsed); 45 | lk_emitstring(echo, 0, "Hello World!"); 46 | return 1000; 47 | } 48 | 49 | static int resp(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 50 | (void)sender; 51 | if (sig != NULL) { 52 | lk_log(S, "res: %s", (char*)sig->data); 53 | lk_close(S); 54 | } 55 | return LK_OK; 56 | } 57 | 58 | static int loki_service_echo(lk_State *S, lk_Slot *sender, lk_Signal *sig) { 59 | (void)sig; 60 | if (sender == NULL) { 61 | lk_Service *svr = lk_launch(S, "timer", loki_service_timer, NULL); 62 | lk_Timer *t; 63 | int *pi = (int*)lk_malloc(S, sizeof(int)); 64 | *pi = 0; 65 | lk_newslot(S, "echo", echo, NULL); 66 | t = lk_newtimer(svr, repeater, (void*)pi); 67 | lk_starttimer(t, 1000); 68 | } 69 | return LK_OK; 70 | } 71 | 72 | int main(void) { 73 | lk_State *S; 74 | (void)lk_initlock(&memlock); 75 | S = lk_newstate(NULL, debug_allocf, NULL); 76 | 77 | /*lk_openlibs(S);*/ 78 | lk_launch(S, "log", loki_service_log, NULL); 79 | lk_setslothandler((lk_Slot*)S, resp); 80 | 81 | lk_log(S, ""); 82 | lk_log(S, "I[]"); 83 | lk_log(S, "I[test]" lk_loc("test test test")); 84 | lk_log(S, "T[test]" lk_loc("test test test")); 85 | lk_log(S, "V[test]" lk_loc("test test test")); 86 | lk_log(S, "W[test]" lk_loc("test test test")); 87 | lk_log(S, "E[test]" lk_loc("你好,世界")); 88 | 89 | lk_launch(S, "echo", loki_service_echo, NULL); 90 | 91 | lk_Slot *slot = lk_slot(S, "echo.echo"); 92 | lk_emitstring(slot, 0, "Hello World!"); 93 | 94 | lk_log(S, "thread count: %d", lk_start(S, 0)); 95 | lk_waitclose(S); 96 | lk_close(S); 97 | printf("totalmem = %d\n", (int)totalmem); 98 | assert(totalmem == 0); 99 | return 0; 100 | } 101 | 102 | /* cc: flags+='-Wextra -ggdb -O0' input+='service_*.c' 103 | * unixcc: libs+='-pthread -ldl' 104 | * win32cc: libs+='-lws2_32' */ 105 | 106 | -------------------------------------------------------------------------------- /loki_services.h: -------------------------------------------------------------------------------- 1 | #ifndef loki_services_h 2 | #define loki_services_h 3 | 4 | 5 | #include "loki.h" 6 | #include "lk_buffer.h" 7 | 8 | LK_NS_BEGIN 9 | 10 | 11 | /* services */ 12 | 13 | LKMOD_API lk_Handler loki_service_listener; 14 | LKMOD_API lk_Handler loki_service_loader; 15 | LKMOD_API lk_Handler loki_service_log; 16 | LKMOD_API lk_Handler loki_service_socket; 17 | LKMOD_API lk_Handler loki_service_timer; 18 | 19 | 20 | /* listener interface */ 21 | 22 | LK_API int lk_addlistener (lk_Service *svr, lk_Slot *slot, lk_Handler *h, void *ud); 23 | LK_API int lk_dellistener (lk_Service *svr, lk_Slot *slot, lk_Handler *h, void *ud); 24 | 25 | 26 | /* loader interface */ 27 | 28 | typedef struct lk_Loader lk_Loader; 29 | 30 | typedef int lk_LoaderHandler (lk_State *S, void *ud, lk_Loader *l, const char *name); 31 | 32 | LK_API lk_Data *lk_searchpath (lk_Loader *loader, const char *paths, const char *name); 33 | LK_API int lk_loaderror (lk_Loader *loader, const char *msg, ...); 34 | LK_API int lk_loadverror (lk_Loader *loader, const char *msg, va_list l); 35 | LK_API void lk_sethandler (lk_Loader *loader, lk_Handler *h, void *data); 36 | LK_API void lk_setdeletor (lk_Loader *loader, lk_Handler *h, void *data); 37 | 38 | LK_API void lk_preload (lk_Service *svr, const char *name, lk_Handler *h); 39 | 40 | LK_API void lk_addloader (lk_Service *svr, lk_LoaderHandler *h, void *ud); 41 | LK_API void lk_delloader (lk_Service *svr, lk_LoaderHandler *h, void *ud); 42 | 43 | LK_API lk_Service *lk_require (lk_Service *svr, const char *name); 44 | 45 | 46 | /* timer interface */ 47 | 48 | #ifndef lk_Time 49 | # ifdef LOKI_USE_64BIT_TIMER 50 | typedef unsigned long long lk_Time; 51 | # else 52 | typedef unsigned lk_Time; 53 | # endif 54 | # define lk_Time lk_Time 55 | #endif /* lk_Time */ 56 | 57 | typedef struct lk_Timer lk_Timer; 58 | 59 | typedef lk_Time lk_TimerHandler (lk_State *S, void *ud, lk_Timer *timer, lk_Time delayed); 60 | 61 | LK_API lk_Time lk_time(void); 62 | 63 | LK_API lk_Timer *lk_newtimer (lk_Service *svr, lk_TimerHandler *h, void *ud); 64 | LK_API void lk_deltimer (lk_Timer *timer); 65 | 66 | LK_API void lk_starttimer (lk_Timer *timer, lk_Time delayms); 67 | LK_API void lk_canceltimer (lk_Timer *timer); 68 | 69 | 70 | /* socket interface */ 71 | 72 | typedef struct lk_Accept lk_Accept; 73 | typedef struct lk_Tcp lk_Tcp; 74 | typedef struct lk_Udp lk_Udp; 75 | 76 | typedef void lk_AcceptHandler (lk_State *S, void *ud, unsigned err, lk_Accept *accept, lk_Tcp *tcp); 77 | typedef void lk_ConnectHandler (lk_State *S, void *ud, unsigned err, lk_Tcp *tcp); 78 | typedef void lk_UdpBindHandler (lk_State *S, void *ud, unsigned err, lk_Udp *udp); 79 | 80 | typedef size_t lk_HeaderHandler (lk_State *S, void *ud, lk_Tcp *tcp, const char *buff, size_t len); 81 | typedef void lk_PacketHandler (lk_State *S, void *ud, lk_Tcp *tcp, const char *buff, size_t len); 82 | typedef void lk_RecvFromHandler (lk_State *S, void *ud, lk_Udp *udp, unsigned err, 83 | const char *buff, unsigned count, 84 | const char *addr, unsigned port); 85 | 86 | LK_API void lk_setonheader (lk_Service *svr, lk_HeaderHandler *h, void *ud); 87 | LK_API void lk_setonpacket (lk_Service *svr, lk_PacketHandler *h, void *ud); 88 | LK_API void lk_setonudpmsg (lk_Service *svr, lk_RecvFromHandler *h, void *ud); 89 | 90 | LK_API lk_Accept *lk_newaccept (lk_Service *svr, lk_AcceptHandler *h, void *ud); 91 | LK_API void lk_delaccept (lk_Accept *accept); 92 | 93 | LK_API void lk_listen (lk_Accept *accept, const char *addr, unsigned port); 94 | 95 | LK_API void lk_connect (lk_Service *svr, const char *addr, unsigned port, 96 | lk_ConnectHandler *h, void *ud); 97 | LK_API void lk_deltcp (lk_Tcp *tcp); 98 | 99 | LK_API void *lk_gettcpdata (lk_Tcp *tcp); 100 | LK_API void lk_settcpdata (lk_Tcp *tcp, void *data); 101 | 102 | LK_API void lk_send (lk_Tcp *tcp, const char *buff, unsigned size); 103 | 104 | LK_API void lk_bindudp (lk_Service *svr, const char *addr, unsigned port, 105 | lk_UdpBindHandler *h, void *ud); 106 | LK_API void lk_deludp (lk_Udp *udp); 107 | 108 | LK_API void lk_sendto (lk_Udp *udp, const char *buff, unsigned len, 109 | const char *addr, unsigned port); 110 | 111 | 112 | LK_NS_END 113 | 114 | #endif /* loki_services_h */ 115 | 116 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 117 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 118 | * cc: flags+='-Wextra -O3' input='service_*.c lokilib.c' */ 119 | 120 | -------------------------------------------------------------------------------- /lk_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef lk_buffer_h 2 | #define lk_buffer_h 3 | 4 | 5 | #include "loki.h" 6 | 7 | #define LK_BUFFERSIZE 1024 8 | 9 | 10 | LK_NS_BEGIN 11 | 12 | typedef struct lk_Buffer { 13 | size_t size; 14 | size_t capacity; 15 | lk_State *S; 16 | char *buff; 17 | char init_buff[LK_BUFFERSIZE]; 18 | } lk_Buffer; 19 | 20 | #define lk_buffer(B) ((B)->buff) 21 | #define lk_buffsize(B) ((B)->size) 22 | #define lk_resetbuffer(B) ((B)->size = 0) 23 | #define lk_addchar(B,ch) (*lk_prepbuffsize((B), 1) = (ch), ++(B)->size) 24 | #define lk_addstring(B,s) lk_addlstring((B),(s),strlen(s)) 25 | 26 | LK_API void lk_initbuffer (lk_State *S, lk_Buffer *b); 27 | LK_API void lk_freebuffer (lk_Buffer *b); 28 | 29 | LK_API char *lk_prepbuffsize (lk_Buffer *B, size_t len); 30 | 31 | LK_API size_t lk_adddata (lk_Buffer *B, lk_Data *data); 32 | LK_API size_t lk_addsize (lk_Buffer *B, int size); 33 | LK_API size_t lk_addlstring (lk_Buffer *B, const char *s, size_t len); 34 | LK_API size_t lk_addvfstring (lk_Buffer *B, const char *fmt, va_list l); 35 | LK_API size_t lk_addfstring (lk_Buffer *B, const char *fmt, ...); 36 | 37 | LK_API void lk_replacebuffer (lk_Buffer *B, char origch, char newch); 38 | 39 | LK_API lk_Data *lk_buffresult (lk_Buffer *B); 40 | 41 | LK_NS_END 42 | 43 | 44 | #endif /* lk_buffer_h */ 45 | 46 | #if defined(LOKI_IMPLEMENTATION) && !defined(lk_buffer_implemented) 47 | #define lk_buffer_implemented 48 | 49 | 50 | LK_NS_BEGIN 51 | 52 | LK_API void lk_initbuffer (lk_State *S, lk_Buffer *B) { 53 | B->size = 0; 54 | B->S = S; 55 | B->capacity = LK_BUFFERSIZE; 56 | B->buff = B->init_buff; 57 | } 58 | 59 | LK_API void lk_freebuffer (lk_Buffer *B) { 60 | if (B->buff != B->init_buff) 61 | lk_free(B->S, B->buff, B->capacity); 62 | lk_initbuffer(B->S, B); 63 | } 64 | 65 | LK_API char *lk_prepbuffsize (lk_Buffer *B, size_t len) { 66 | if (B->size + len > B->capacity) { 67 | void *newptr; 68 | size_t newsize = LK_BUFFERSIZE; 69 | while (newsize < B->size + len && newsize < ~(size_t)0/2) 70 | newsize *= 2; 71 | if (B->buff != B->init_buff) 72 | newptr = lk_realloc(B->S, B->buff, newsize, B->capacity); 73 | else { 74 | newptr = lk_malloc(B->S, newsize); 75 | memcpy(newptr, B->buff, B->size); 76 | } 77 | B->buff = (char*)newptr; 78 | B->capacity = newsize; 79 | } 80 | return &B->buff[B->size]; 81 | } 82 | 83 | LK_API size_t lk_adddata (lk_Buffer *B, lk_Data *data) { 84 | size_t len = lk_len(data); 85 | memcpy(lk_prepbuffsize(B, len), (char*)data, len); 86 | return B->size += len; 87 | } 88 | 89 | LK_API size_t lk_addsize (lk_Buffer *B, int size) { 90 | size_t capacity = B->capacity; 91 | if (size < 0 && (size_t)0 - size > B->size) 92 | return B->size = 0; 93 | if (size > 0 && B->size + size > capacity) { 94 | lk_prepbuffsize(B, size); 95 | memset(B->buff+capacity, 0, B->size+size-capacity); 96 | } 97 | return B->size += size; 98 | } 99 | 100 | LK_API size_t lk_addlstring (lk_Buffer *B, const char *s, size_t len) { 101 | memcpy(lk_prepbuffsize(B, len), s, len); 102 | return B->size += len; 103 | } 104 | 105 | LK_API size_t lk_addvfstring (lk_Buffer *B, const char *fmt, va_list l) { 106 | const size_t init_size = 80; 107 | char *ptr = lk_prepbuffsize(B, init_size+1); 108 | va_list l_count; 109 | int len; 110 | #ifdef va_copy 111 | va_copy(l_count, l); 112 | #else 113 | __va_copy(l_count, l); 114 | #endif 115 | len = lk_vsnprintf(ptr, init_size, fmt, l_count); 116 | va_end(l_count); 117 | if (len <= 0) return 0; 118 | if ((size_t)len > init_size) { 119 | ptr = lk_prepbuffsize(B, len + 1); 120 | lk_vsnprintf(ptr, len+1, fmt, l); 121 | } 122 | return B->size += len; 123 | } 124 | 125 | LK_API size_t lk_addfstring (lk_Buffer *B, const char *fmt, ...) { 126 | size_t ret; 127 | va_list l; 128 | va_start(l, fmt); 129 | ret = lk_addvfstring(B, fmt, l); 130 | va_end(l); 131 | return ret; 132 | } 133 | 134 | LK_API void lk_replacebuffer (lk_Buffer *B, char origch, char newch) { 135 | size_t i; 136 | for (i = 0; i < B->size; ++i) { 137 | if (B->buff[i] == origch) 138 | B->buff[i] = newch; 139 | } 140 | } 141 | 142 | LK_API lk_Data *lk_buffresult (lk_Buffer *B) { 143 | lk_Data *result = lk_newdata(B->S, B->size+1); 144 | memcpy(result, B->buff, B->size); 145 | ((char*)result)[B->size] = '\0'; 146 | lk_setlen(result, B->size); 147 | lk_freebuffer(B); 148 | return result; 149 | } 150 | 151 | LK_NS_END 152 | 153 | 154 | #endif /* LOKI_IMPLEMENTATION */ 155 | 156 | /* win32cc: flags+='-Wextra -s -O3 -mdll -DLOKI_IMPLEMENTATION -std=c90 -pedantic -xc' 157 | * win32cc: output='loki.dll' 158 | * unixcc: flags+='-Wextra -s -O3 -fPIC -shared -DLOKI_IMPLEMENTATION -xc' 159 | * unixcc: output='loki.so' */ 160 | 161 | -------------------------------------------------------------------------------- /service_listener.c: -------------------------------------------------------------------------------- 1 | #define LOKI_MODULE 2 | #include "loki_services.h" 3 | 4 | #include 5 | 6 | 7 | typedef struct lk_ListenerState lk_ListenerState; 8 | typedef struct lk_Listener lk_Listener; 9 | typedef struct lk_SlotEntry lk_SlotEntry; 10 | 11 | struct lk_ListenerState { 12 | lk_State *S; 13 | lk_Lock lock; 14 | lk_Table svrmap; 15 | lk_Table slotmap; 16 | lk_MemPool listeners; 17 | }; 18 | 19 | struct lk_Listener { 20 | lk_Source source; 21 | lk_SlotEntry *entry; 22 | lk_Listener *link; 23 | lk_Listener *next; 24 | lk_Listener *prev; 25 | }; 26 | 27 | struct lk_SlotEntry { 28 | lk_Entry base; 29 | lk_ListenerState *ls; 30 | lk_Slot *target; 31 | lk_Listener *listeners; 32 | }; 33 | 34 | #define lkX_svrstate(svr) ((lk_ListenerState*)lk_data((lk_Slot*)(svr))) 35 | #define lkX_state(S) ((lk_ListenerState*)lk_userdata(S)) 36 | 37 | static void lkX_link (lk_Listener **pp, lk_Listener *node) { 38 | if (*pp == NULL) 39 | *pp = node->prev = node->next = node; 40 | else { 41 | lk_Listener *h = *pp; 42 | node->prev = h->prev; 43 | node->next = h; 44 | h->prev->next = node; 45 | h->prev = node; 46 | } 47 | } 48 | 49 | static void lkX_unlink (lk_Listener **pp, lk_Listener *node) { 50 | if (*pp == node) *pp = node == node->next ? NULL : node->next; 51 | node->prev->next = node->next; 52 | node->next->prev = node->prev; 53 | } 54 | 55 | static unsigned lkX_ptrhash (unsigned p) { 56 | p = (p+0x7ed55d16) + (p<<12); 57 | p = (p^0xc761c23c) ^ (p>>19); 58 | p = (p+0x165667b1) + (p<<5); 59 | p = (p+0xd3a2646c) ^ (p<<9); 60 | p = (p+0xfd7046c5) + (p<<3); 61 | p = (p^0xb55a4f09) ^ (p>>16); 62 | return p; 63 | } 64 | 65 | static lk_Entry *lkX_gettable (lk_Table *t, void *key) { 66 | unsigned hash; 67 | lk_Entry *e; 68 | if (t->size == 0 || key == NULL) return NULL; 69 | hash = lkX_ptrhash((unsigned)(ptrdiff_t)key); 70 | e = (lk_Entry*)((char*)t->hash + ((hash & (t->size - 1))*t->entry_size)); 71 | for (;;) { 72 | if (e->key && (e->hash == hash && strcmp(e->key, key) == 0)) 73 | return e; 74 | if (e->next == 0) return NULL; 75 | e = (lk_Entry*)((char*)e + e->next); 76 | } 77 | return e; 78 | } 79 | 80 | static lk_Entry *lkX_settable (lk_State *S, lk_Table *t, void *key) { 81 | lk_Entry e, *ret; 82 | if (key == NULL) return NULL; 83 | if ((ret = lkX_gettable(t, key)) != NULL) 84 | return ret; 85 | e.key = key; 86 | e.hash = lkX_ptrhash((unsigned)(ptrdiff_t)key); 87 | return lk_newkey(S, t, &e); 88 | } 89 | 90 | static int lkX_next (lk_Listener *h, lk_Listener **pnode, lk_Listener **pnext) { 91 | if (h == NULL) return 0; 92 | if (*pnode != NULL) { 93 | lk_Listener *next = pnext ? *pnext : (*pnode)->next; 94 | if (next != h) { 95 | if (pnext) *pnext = next; 96 | *pnode = next; 97 | return 1; 98 | } 99 | *pnode = NULL; 100 | return 0; 101 | } 102 | *pnode = h; 103 | if (pnext) *pnext = h->next; 104 | return 1; 105 | } 106 | 107 | static int lkX_srcdeletor (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 108 | lk_Listener *node = (lk_Listener*)sig->source; 109 | lk_SlotEntry *list = node->entry; 110 | lk_ListenerState *ls = list->ls; 111 | (void)S, (void)sender; 112 | lk_lock(ls->lock); 113 | lkX_unlink(&list->listeners, node); 114 | lk_poolfree(&list->ls->listeners, node); 115 | if (list->listeners == NULL) { 116 | lk_Entry *e = lkX_gettable(&ls->slotmap, list->target); 117 | e->key = NULL; 118 | lk_sethook(list->target, NULL, NULL); 119 | } 120 | lk_unlock(ls->lock); 121 | return LK_OK; 122 | } 123 | 124 | static int lkX_broadcast (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 125 | lk_Source *src = sig->source; 126 | lk_SlotEntry *list = (lk_SlotEntry*)lk_userdata(S); 127 | lk_ListenerState *ls = list->ls; 128 | lk_Listener *node = NULL; 129 | (void)sender; 130 | lk_lock(ls->lock); 131 | while (lkX_next(list->listeners, &node, NULL)) { 132 | sig->source = &node->source; 133 | lk_emit((lk_Slot*)node->source.service, sig); 134 | } 135 | lk_unlock(ls->lock); 136 | sig->source = src; 137 | return LK_OK; 138 | } 139 | 140 | static int lkX_addlistener (lk_ListenerState *ls, lk_Slot *slot, lk_Handler *h, void *ud) { 141 | lk_Listener *node; 142 | lk_PtrEntry *e = (lk_PtrEntry*) 143 | lkX_gettable(&ls->svrmap, lk_service(slot)); 144 | lk_SlotEntry *list; 145 | if (e == NULL) return LK_ERR; 146 | list = (lk_SlotEntry*)lkX_settable(ls->S, &ls->slotmap, slot); 147 | if (list->ls == NULL) { 148 | list->ls = ls; 149 | list->target = slot; 150 | lk_sethook(slot, lkX_broadcast, list); 151 | } 152 | node = (lk_Listener*)lk_poolalloc(ls->S, &ls->listeners); 153 | lk_initsource(ls->S, &node->source, h, ud); 154 | node->source.deletor = lkX_srcdeletor; 155 | node->source.refcount = 1; 156 | node->source.force = 1; 157 | node->entry = list; 158 | node->link = (lk_Listener*)e->data; 159 | e->data = node; 160 | lkX_link(&list->listeners, node); 161 | return LK_OK; 162 | } 163 | 164 | static lk_Listener *lkX_dellistener (lk_ListenerState *ls, lk_Slot *slot, lk_Handler *h, void *ud) { 165 | lk_Listener **pp, *node = NULL; 166 | lk_PtrEntry *e = (lk_PtrEntry*)lkX_gettable(&ls->slotmap, slot); 167 | for (pp = (lk_Listener**)&e->data; *pp != NULL; pp = &(*pp)->link) 168 | if ((*pp)->source.callback == h && (*pp)->source.ud == ud) 169 | break; 170 | if (*pp != NULL) { 171 | node = *pp; 172 | *pp = (*pp)->link; 173 | } 174 | return node; 175 | } 176 | 177 | static int lkX_launch (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 178 | lk_ListenerState *ls = (lk_ListenerState*)lk_userdata(S); 179 | lk_Service *svr = (lk_Service*)sig->data; 180 | lk_PtrEntry *e; 181 | (void)sender; 182 | lk_lock(ls->lock); 183 | e = (lk_PtrEntry*)lkX_settable(S, &ls->svrmap, svr); 184 | if (lk_key(e) == NULL) { 185 | lk_key(e) = (const char*)svr; 186 | e->data = NULL; 187 | } 188 | lk_unlock(ls->lock); 189 | return LK_OK; 190 | } 191 | 192 | static int lkX_close (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 193 | lk_ListenerState *ls = lkX_state(S); 194 | lk_PtrEntry *e; 195 | lk_Listener *node = NULL; 196 | (void)sender; 197 | lk_lock(ls->lock); 198 | if ((e = (lk_PtrEntry*)lkX_gettable(&ls->svrmap, sig->data)) != NULL) { 199 | lk_key(e) = NULL; 200 | node = (lk_Listener*)e->data; 201 | e->data = NULL; 202 | } 203 | lk_unlock(ls->lock); 204 | while (node) { 205 | lk_Listener *next = node->link; 206 | lk_freesource(&node->source); 207 | node = next; 208 | } 209 | return LK_OK; 210 | } 211 | 212 | LK_API int lk_addlistener (lk_Service *svr, lk_Slot *slot, lk_Handler *h, void *ud) { 213 | lk_ListenerState *ls = lkX_svrstate(svr); 214 | int ret = LK_ERR; 215 | if (h != NULL) { 216 | lk_lock(ls->lock); 217 | ret = lkX_addlistener(ls, slot, h, ud); 218 | lk_unlock(ls->lock); 219 | } 220 | return ret; 221 | } 222 | 223 | LK_API int lk_dellistener (lk_Service *svr, lk_Slot *slot, lk_Handler *h, void *ud) { 224 | lk_ListenerState *ls = lkX_svrstate(svr); 225 | lk_Listener *node; 226 | lk_lock(ls->lock); 227 | node = lkX_dellistener(ls, slot, h, ud); 228 | lk_unlock(ls->lock); 229 | if (node != NULL) { 230 | lk_freesource(&node->source); 231 | return LK_OK; 232 | } 233 | return LK_ERR; 234 | } 235 | 236 | LKMOD_API int loki_service_listener (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 237 | if (sender == NULL) { /* init */ 238 | lk_ListenerState *ls = (lk_ListenerState*) 239 | lk_malloc(S, sizeof(lk_ListenerState)); 240 | memset(ls, 0, sizeof(*ls)); 241 | ls->S = S; 242 | if (!lk_initlock(&ls->lock)) return LK_ERR; 243 | lk_initpool(&ls->listeners, sizeof(lk_Listener)); 244 | lk_inittable(&ls->svrmap, sizeof(lk_PtrEntry)); 245 | lk_inittable(&ls->slotmap, sizeof(lk_SlotEntry)); 246 | lk_newslot(S, LK_SLOTNAME_LAUNCH, lkX_launch, ls); 247 | lk_newslot(S, LK_SLOTNAME_CLOSE, lkX_close, ls); 248 | lkX_settable(S, &ls->svrmap, S); 249 | lk_setdata(lk_current(S), ls); 250 | return LK_WEAK; 251 | } 252 | else if (sig == NULL) { /* free */ 253 | lk_ListenerState *ls = lkX_state(S); 254 | lk_freetable(S, &ls->svrmap); 255 | lk_freetable(S, &ls->slotmap); 256 | lk_freepool(S, &ls->listeners); 257 | lk_freelock(ls->lock); 258 | lk_free(S, ls, sizeof(*ls)); 259 | } 260 | return LK_OK; 261 | } 262 | 263 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 264 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 265 | * cc: flags+='-Wextra -O3' input+='lokilib.c' */ 266 | 267 | -------------------------------------------------------------------------------- /service_timer.c: -------------------------------------------------------------------------------- 1 | #define LOKI_MODULE 2 | #include "loki_services.h" 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __APPLE__ 8 | # include 9 | # include 10 | #endif 11 | 12 | 13 | #ifdef _WIN32 14 | 15 | LK_API lk_Time lk_time(void) { 16 | static LARGE_INTEGER counterFreq, startTime; 17 | LARGE_INTEGER current; 18 | if (counterFreq.QuadPart == 0) { 19 | QueryPerformanceFrequency(&counterFreq); 20 | QueryPerformanceCounter(&startTime); 21 | assert(counterFreq.HighPart == 0); 22 | } 23 | QueryPerformanceCounter(¤t); 24 | return (lk_Time)((current.QuadPart - startTime.QuadPart) * 1000 25 | / counterFreq.LowPart); 26 | } 27 | 28 | #else 29 | 30 | LK_API lk_Time lk_time(void) { 31 | #ifdef __APPLE__ 32 | static mach_timebase_info_data_t time_info; 33 | static uint64_t start; 34 | if (!time_info.numer) { 35 | start = mach_absolute_time(); 36 | (void)mach_timebase_info(&time_info); 37 | } 38 | uint64_t now = mach_absolute_time(); 39 | return (lk_Time)((now - start) * time_info.numer / time_info.denom / 1000000); 40 | #else 41 | static lk_Time start = ~(lk_Time)0; 42 | struct timespec ts; 43 | lk_Time time; 44 | if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) 45 | return 1; 46 | time = (lk_Time)((lk_Time)ts.tv_sec*1000+ts.tv_nsec/1000000); 47 | if (start == ~(lk_Time)0) { 48 | start = time; 49 | return 0; 50 | } 51 | return time - start; 52 | #endif 53 | } 54 | 55 | #endif 56 | 57 | 58 | /* implements */ 59 | 60 | #define LK_TIMER_NOINDEX (~(unsigned)0) 61 | #define LK_FOREVER (~(lk_Time)0) 62 | #ifndef LK_MAX_SIZET 63 | # define LK_MAX_SIZET ((~(size_t)0)-100) 64 | #endif 65 | 66 | #define LK_MIN_TIMEHEAP 512 67 | 68 | typedef struct lk_TimerState lk_TimerState; 69 | 70 | struct lk_Timer { 71 | union { lk_Timer *next; void *ud; } u; 72 | lk_TimerHandler *handler; 73 | lk_Service *service; 74 | lk_TimerState *ts; 75 | unsigned index; 76 | lk_Time starttime; 77 | lk_Time emittime; 78 | }; 79 | 80 | struct lk_TimerState { 81 | lk_State *S; 82 | lk_MemPool timers; 83 | lk_Timer **heap; 84 | lk_Slot *poll; 85 | lk_Lock lock; 86 | lk_Time nexttime; 87 | unsigned heap_used; 88 | unsigned heap_size; 89 | }; 90 | 91 | static int lkX_resizeheap (lk_TimerState *ts, size_t size) { 92 | lk_Timer **heap; 93 | size_t realsize = LK_MIN_TIMEHEAP; 94 | while (realsize < size && realsize < LK_MAX_SIZET/sizeof(lk_Timer*)/2) 95 | realsize <<= 1; 96 | if (realsize < size) return 0; 97 | heap = (lk_Timer**)lk_realloc(ts->S, ts->heap, 98 | realsize*sizeof(lk_Timer*), ts->heap_size*sizeof(lk_Timer*)); 99 | ts->heap = heap; 100 | ts->heap_size = (unsigned)realsize; 101 | return 1; 102 | } 103 | 104 | static void lkX_canceltimer (lk_TimerState *ts, lk_Timer *timer) { 105 | unsigned index = timer->index; 106 | if (index == LK_TIMER_NOINDEX) return; 107 | timer->index = LK_TIMER_NOINDEX; 108 | if (ts->heap_used == 0 || timer == ts->heap[--ts->heap_used]) 109 | return; 110 | timer = ts->heap[ts->heap_used]; 111 | while (1) { 112 | unsigned left = (index<<1)|1, right = (index+1)<<1; 113 | unsigned newindex = right; 114 | if (left >= ts->heap_used) break; 115 | if (timer->emittime >= ts->heap[left]->emittime) { 116 | if (right >= ts->heap_used 117 | || ts->heap[left]->emittime < ts->heap[right]->emittime) 118 | newindex = left; 119 | } 120 | else if (right >= ts->heap_used 121 | || timer->emittime <= ts->heap[right]->emittime) 122 | break; 123 | ts->heap[index] = ts->heap[newindex]; 124 | ts->heap[index]->index = index; 125 | index = newindex; 126 | } 127 | ts->heap[index] = timer; 128 | timer->index = index; 129 | } 130 | 131 | static void lkX_starttimer (lk_TimerState *ts, lk_Timer *timer, lk_Time delayms) { 132 | unsigned index; 133 | lkX_canceltimer(ts, timer); 134 | if (ts->heap_size <= ts->heap_used) 135 | lkX_resizeheap(ts, ts->heap_size * 2); 136 | index = ts->heap_used++; 137 | timer->starttime = lk_time(); 138 | timer->emittime = timer->starttime + delayms; 139 | while (index) { 140 | unsigned parent = (index-1)>>1; 141 | if (ts->heap[parent]->emittime <= timer->emittime) 142 | break; 143 | ts->heap[index] = ts->heap[parent]; 144 | ts->heap[index]->index = index; 145 | index = parent; 146 | } 147 | ts->heap[index] = timer; 148 | timer->index = index; 149 | if (index == 0) { 150 | lk_Signal sig = LK_SIGNAL; 151 | ts->nexttime = timer->emittime; 152 | lk_emit(ts->poll, &sig); 153 | } 154 | } 155 | 156 | LK_API lk_Timer *lk_newtimer (lk_Service *svr, lk_TimerHandler *cb, void *ud) { 157 | lk_TimerState *ts = (lk_TimerState*)lk_data((lk_Slot*)svr); 158 | lk_Timer *timer; 159 | lk_lock(ts->lock); 160 | timer = (lk_Timer*)lk_poolalloc(ts->S, &ts->timers); 161 | timer->u.ud = ud; 162 | timer->handler = cb; 163 | timer->ts = ts; 164 | timer->service = NULL; 165 | timer->index = LK_TIMER_NOINDEX; 166 | lk_unlock(ts->lock); 167 | return timer; 168 | } 169 | 170 | LK_API void lk_deltimer (lk_Timer *timer) { 171 | lk_TimerState *ts = timer->ts; 172 | lk_lock(ts->lock); 173 | if (timer->service) { 174 | lk_release(timer->service); 175 | timer->service = NULL; 176 | } 177 | lkX_canceltimer(ts, timer); 178 | lk_poolfree(&ts->timers, timer); 179 | lk_unlock(ts->lock); 180 | } 181 | 182 | LK_API void lk_starttimer (lk_Timer *timer, lk_Time delayms) { 183 | lk_TimerState *ts = timer->ts; 184 | lk_Service *self = lk_self(ts->S); 185 | lk_lock(ts->lock); 186 | if (timer->service != self) { 187 | lk_release(timer->service); 188 | lk_retain(self); 189 | timer->service = self; 190 | } 191 | lkX_starttimer(ts, timer, delayms); 192 | lk_unlock(ts->lock); 193 | } 194 | 195 | LK_API void lk_canceltimer (lk_Timer *timer) { 196 | lk_TimerState *ts = timer->ts; 197 | lk_lock(ts->lock); 198 | if (timer->service) { 199 | lk_release(timer->service); 200 | timer->service = NULL; 201 | } 202 | lkX_canceltimer(ts, timer); 203 | lk_unlock(ts->lock); 204 | } 205 | 206 | static void lkX_updatetimers (lk_TimerState *ts, lk_Time current) { 207 | if (ts->nexttime > current) return; 208 | while (ts->heap_used && ts->heap[0]->emittime <= current) { 209 | lk_Signal sig = LK_RESPONSE; 210 | lk_Timer *timer = ts->heap[0]; 211 | lkX_canceltimer(ts, timer); 212 | timer->emittime = current; 213 | sig.data = timer; 214 | lk_emit((lk_Slot*)timer->service, &sig); 215 | } 216 | ts->nexttime = ts->heap_used == 0 ? LK_FOREVER : ts->heap[0]->emittime; 217 | } 218 | 219 | static lk_TimerState *lkX_newstate (lk_State *S) { 220 | lk_TimerState *ts = (lk_TimerState*) 221 | lk_malloc(S, sizeof(lk_TimerState)); 222 | memset(ts, 0, sizeof(*ts)); 223 | if (!lk_initlock(&ts->lock)) 224 | lk_discard(S); 225 | ts->S = S; 226 | lk_initpool(&ts->timers, sizeof(lk_Timer)); 227 | return ts; 228 | } 229 | 230 | static int lkX_poller (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 231 | lk_TimerState *ts = (lk_TimerState*)lk_data(sender); 232 | lk_Time nexttime, current; 233 | for (;;) { 234 | int waittime; 235 | lk_lock(ts->lock); 236 | lkX_updatetimers(ts, current = lk_time()); 237 | nexttime = ts->nexttime; 238 | assert(nexttime > current); 239 | lk_unlock(ts->lock); 240 | waittime = nexttime == LK_FOREVER ? -1 241 | : (int)(nexttime - current); 242 | if (lk_wait(S, sig, waittime) == LK_ERR) 243 | break; 244 | } 245 | ts->nexttime = LK_FOREVER; 246 | lk_freepool(S, &ts->timers); 247 | lk_freelock(ts->lock); 248 | lk_free(S, ts->heap, ts->heap_size * sizeof(lk_Timer*)); 249 | lk_free(S, ts, sizeof(lk_TimerState)); 250 | return LK_OK; 251 | } 252 | 253 | static int lkX_refactor (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 254 | lk_Timer *timer = (lk_Timer*)sig->data; 255 | (void)sender; 256 | if (timer->handler) { 257 | int ret = timer->handler(S, timer->u.ud, timer, 258 | timer->emittime - timer->starttime); 259 | if (ret > 0) lk_starttimer(timer, ret); 260 | else lk_deltimer(timer); 261 | } 262 | return LK_OK; 263 | } 264 | 265 | LKMOD_API int loki_service_timer (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 266 | (void)sig; 267 | if (sender == NULL) { 268 | lk_TimerState *ts = lkX_newstate(S); 269 | lk_Service *svr = lk_self(S); 270 | ts->poll = lk_newpoll(S, "poll", lkX_poller, ts); 271 | lk_setrefactor((lk_Slot*)svr, lkX_refactor); 272 | lk_setdata((lk_Slot*)svr, ts); 273 | return LK_WEAK; 274 | } 275 | return LK_OK; 276 | } 277 | 278 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 279 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 280 | * cc: flags+='-Wextra -O3' input='service_*.c lokilib.c' */ 281 | 282 | -------------------------------------------------------------------------------- /service_loader.c: -------------------------------------------------------------------------------- 1 | #define LOKI_MODULE 2 | #include "loki_services.h" 3 | #include "lk_buffer.h" 4 | 5 | #include 6 | 7 | #ifndef _WIN32 8 | # include 9 | # include 10 | # include 11 | #endif 12 | 13 | #ifdef __APPLE__ 14 | # include 15 | #endif 16 | 17 | typedef lkQ_type(struct lk_LoaderNode) lk_LoaderList; 18 | 19 | typedef struct lk_HandlerEntry { 20 | lk_Entry entry; 21 | lk_Handler *handler; 22 | } lk_HandlerEntry; 23 | 24 | typedef struct lk_LoaderNode { 25 | lkQ_entry(struct lk_LoaderNode); 26 | lk_LoaderHandler *loader; 27 | void *ud; 28 | } lk_LoaderNode; 29 | 30 | typedef struct lk_LoaderState { 31 | lk_State *S; 32 | lk_Service *monitor; 33 | unsigned in_require : 1; 34 | lk_Table preloads; 35 | lk_Table modules; 36 | lk_MemPool loader_pool; 37 | lk_LoaderList loader_list; 38 | lk_Data *binpath; 39 | lk_Lock lock; 40 | } lk_LoaderState; 41 | 42 | struct lk_Loader { 43 | lk_State *S; 44 | const char *module; 45 | lk_Buffer name; 46 | lk_Buffer error; 47 | lk_Data *binpath; 48 | lk_Handler *handler; 49 | void *data; 50 | lk_Handler *deletor; 51 | void *deletor_ud; 52 | }; 53 | 54 | static lk_Data *lkP_getmodulename (lk_State *S) { 55 | #ifdef _WIN32 56 | char buff[MAX_PATH]; 57 | DWORD size; 58 | if ((size = GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH)) == 0) 59 | #elif defined(__APPLE__) 60 | char buff[PROC_PIDPATHINFO_MAXSIZE]; 61 | int size; 62 | if ((size = proc_pidpath(0, buff, sizeof(buff))) == 0) 63 | #else 64 | char buff[PATH_MAX]; 65 | ssize_t size; 66 | if ((size = readlink("/proc/self/exe", buff, PATH_MAX)) <= 0) 67 | #endif 68 | return lk_newstring(S, "."); 69 | else { 70 | while (--size != 0 && buff[size] != '/' && buff[size] != '\\') 71 | ; 72 | return lk_newlstring(S, buff, size); 73 | } 74 | } 75 | 76 | static int lkP_readable (const char *path) { 77 | #ifdef _WIN32 78 | HANDLE hFile = INVALID_HANDLE_VALUE; 79 | int bytes = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); 80 | if (bytes != 0) { 81 | WCHAR *wp = NULL; 82 | wp = (WCHAR*)malloc(sizeof(WCHAR)*bytes); 83 | if (wp != NULL) { 84 | MultiByteToWideChar(CP_UTF8, 0, path, -1, wp, bytes); 85 | hFile = CreateFileW(wp, /* file to open */ 86 | FILE_WRITE_ATTRIBUTES, /* open only for handle */ 87 | FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 88 | NULL, /* default security */ 89 | OPEN_EXISTING, /* existing file only */ 90 | FILE_FLAG_BACKUP_SEMANTICS, /* open directory also */ 91 | NULL); /* no attr. template */ 92 | free(wp); 93 | if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); 94 | return hFile != INVALID_HANDLE_VALUE; 95 | } 96 | } 97 | hFile = CreateFileA(path, /* file to open */ 98 | FILE_WRITE_ATTRIBUTES, /* open only for handle */ 99 | FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 100 | NULL, /* default security */ 101 | OPEN_EXISTING, /* existing file only */ 102 | FILE_FLAG_BACKUP_SEMANTICS, /* open directory also */ 103 | NULL); /* no attr. template */ 104 | if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); 105 | return hFile != INVALID_HANDLE_VALUE; 106 | #else 107 | int fd = open(path, O_RDONLY|O_NOCTTY); 108 | if (fd >= 0) close(fd); 109 | return fd >= 0; 110 | #endif 111 | } 112 | 113 | 114 | /* loader routines */ 115 | 116 | LK_API void lk_sethandler (lk_Loader *loader, lk_Handler *h, void *data) 117 | { loader->handler = h; loader->data = data; } 118 | 119 | LK_API void lk_setdeletor (lk_Loader *loader, lk_Handler *h, void *data) 120 | { loader->deletor = h; loader->deletor_ud = data; } 121 | 122 | LK_API int lk_loaderror (lk_Loader *loader, const char *msg, ...) { 123 | va_list l; 124 | va_start(l, msg); 125 | lk_addchar(&loader->error, '\t'); 126 | lk_addvfstring(&loader->error, msg, l); 127 | lk_addchar(&loader->error, '\n'); 128 | va_end(l); 129 | return lk_discard(loader->S); 130 | } 131 | 132 | LK_API int lk_loadverror (lk_Loader *loader, const char *msg, va_list l) { 133 | lk_addchar(&loader->error, '\t'); 134 | lk_addvfstring(&loader->error, msg, l); 135 | lk_addchar(&loader->error, '\n'); 136 | return lk_discard(loader->S); 137 | } 138 | 139 | LK_API lk_Data *lk_searchpath (lk_Loader *loader, const char *paths, const char *name) { 140 | lk_Buffer B; 141 | while (*paths != '\0') { 142 | lk_initbuffer(loader->S, &B); 143 | for (; *paths != '\0' && *paths != ';'; ++paths) { 144 | if (*paths == '!') 145 | lk_adddata(&B, loader->binpath); 146 | else if (*paths == '?') 147 | lk_addstring(&B, name); 148 | else 149 | lk_addchar(&B, *paths); 150 | } 151 | *lk_prepbuffsize(&B, 1) = '\0'; 152 | if (lkP_readable(lk_buffer(&B))) 153 | return lk_buffresult(&B); 154 | } 155 | return NULL; 156 | } 157 | 158 | static void lkX_initloader (lk_State *S, lk_Loader *loader, const char *name) { 159 | memset(loader, 0, sizeof(*loader)); 160 | loader->S = S; 161 | lk_initbuffer(S, &loader->name); 162 | lk_initbuffer(S, &loader->error); 163 | lk_addstring(&loader->name, name); 164 | } 165 | 166 | static void lkX_freeloader (lk_Loader *loader) { 167 | lk_freebuffer(&loader->name); 168 | lk_freebuffer(&loader->error); 169 | } 170 | 171 | static int lkX_callhandlers (lk_Loader *loader, lk_LoaderNode *list) { 172 | lk_Context ctx; 173 | lk_State *S = loader->S; 174 | lk_LoaderNode *volatile node = list; 175 | volatile int removed = 0; 176 | lk_pushcontext(S, &ctx, (lk_Slot*)lk_self(S)); 177 | while (loader->handler == NULL && node != NULL) { 178 | lk_LoaderNode *next = node->next; 179 | if (node->loader == NULL) 180 | ++removed; 181 | else { 182 | int ret = LK_OK; 183 | lk_try(S, &ctx, 184 | ret = node->loader(S, node->ud, loader, loader->module)); 185 | if (ret == LK_ERR || ctx.retcode == LK_ERR) 186 | loader->handler = NULL; 187 | } 188 | node = next; 189 | } 190 | lk_popcontext(S, &ctx); 191 | if (loader->handler == NULL) 192 | lk_log(S, lk_loc("E[require] load service error:\n"), 193 | lk_buffer(&loader->error)); 194 | return removed; 195 | } 196 | 197 | static void lkX_sweephandlers (lk_LoaderState *ls) { 198 | lk_LoaderNode **pnode = &ls->loader_list.first; 199 | while (*pnode != NULL) { 200 | lk_LoaderNode **pnext = &(*pnode)->next; 201 | if ((*pnode)->loader != NULL) 202 | pnode = pnext; 203 | else { 204 | lk_poolfree(&ls->loader_pool, *pnode); 205 | *pnode = *pnext; 206 | } 207 | } 208 | } 209 | 210 | static lk_Service *lkX_loadservice (lk_Loader *loader) { 211 | lk_State *S = loader->S; 212 | lk_Service *svr = NULL; 213 | if (loader->handler == NULL) 214 | lk_log(S, lk_loc("E[require] load service error: %s"), 215 | lk_buffer(&loader->error)); 216 | else if ((svr = lk_launch(S, lk_buffer(&loader->name), 217 | loader->handler, loader->data)) == NULL) 218 | lk_log(S, lk_loc("E[require] launch service '%s' error"), 219 | lk_buffer(&loader->name)); 220 | else if (loader->deletor) { /* XXX monitor the deletion of module */ 221 | } 222 | return svr; 223 | } 224 | 225 | 226 | /* interface */ 227 | 228 | #define lkX_state(svr) ((lk_LoaderState*)lk_data((lk_Slot*)svr)) 229 | 230 | LK_API void lk_preload (lk_Service *svr, const char *name, lk_Handler *h) { 231 | lk_LoaderState *ls = lkX_state(svr); 232 | lk_HandlerEntry *e; 233 | lk_lock(ls->lock); 234 | e = (lk_HandlerEntry*)lk_settable(ls->S, &ls->preloads, name); 235 | if (e->handler == NULL) { 236 | lk_key(e) = (char*)lk_newstring(ls->S, name); 237 | e->handler = h; 238 | } 239 | lk_unlock(ls->lock); 240 | } 241 | 242 | LK_API void lk_addloader (lk_Service *svr, lk_LoaderHandler *h, void *ud) { 243 | lk_LoaderState *ls = lkX_state(svr); 244 | lk_LoaderNode *node; 245 | lk_lock(ls->lock); 246 | node = (lk_LoaderNode*)lk_poolalloc(ls->S, &ls->loader_pool); 247 | node->loader = h; 248 | node->ud = ud; 249 | lkQ_enqueue(&ls->loader_list, node); 250 | lk_unlock(ls->lock); 251 | } 252 | 253 | LK_API void lk_delloader (lk_Service *svr, lk_LoaderHandler *h, void *ud) { 254 | lk_LoaderState *ls = lkX_state(svr); 255 | lk_LoaderNode **pnode; 256 | lk_lock(ls->lock); 257 | pnode = &ls->loader_list.first; 258 | for (; *pnode != NULL 259 | && (*pnode)->loader == h && (*pnode)->ud == ud; 260 | pnode = &(*pnode)->next) 261 | ; 262 | if (*pnode != NULL) { 263 | if (ls->in_require) 264 | (*pnode)->loader = NULL; 265 | else { 266 | lk_LoaderNode *next = (*pnode)->next; 267 | lk_poolfree(&ls->loader_pool, *pnode); 268 | *pnode = next; 269 | } 270 | } 271 | lk_unlock(ls->lock); 272 | } 273 | 274 | LK_API lk_Service *lk_require (lk_Service *svr, const char *name) { 275 | lk_LoaderState *ls = lkX_state(svr); 276 | lk_LoaderList list; 277 | lk_Service *loaded; 278 | lk_Loader loader; 279 | int removed; 280 | 281 | lkX_initloader(ls->S, &loader, name); 282 | lk_lock(ls->lock); 283 | list = ls->loader_list; 284 | lkQ_init(&ls->loader_list); 285 | lk_unlock(ls->lock); 286 | 287 | ls->in_require = 1; 288 | removed = lkX_callhandlers(&loader, list.first); 289 | ls->in_require = 0; 290 | 291 | lk_lock(ls->lock); 292 | lkQ_merge(&ls->loader_list, &list); 293 | if (removed) lkX_sweephandlers(ls); 294 | lk_unlock(ls->lock); 295 | 296 | loaded = lkX_loadservice(&loader); 297 | lkX_freeloader(&loader); 298 | return loaded; 299 | } 300 | 301 | LKMOD_API int loki_service_loader (lk_State *S, lk_Slot *slot, lk_Signal *sig) { 302 | lk_LoaderState *ls = (lk_LoaderState*)lk_userdata(S); 303 | if (slot == NULL) { /* init */ 304 | ls = (lk_LoaderState*)lk_malloc(S, sizeof(lk_LoaderState)); 305 | memset(ls, 0, sizeof(*ls)); 306 | ls->S = S; 307 | (void)lk_initlock(&ls->lock); 308 | lk_inittable(&ls->preloads, sizeof(lk_HandlerEntry)); 309 | lk_initpool(&ls->loader_pool, sizeof(lk_LoaderNode)); 310 | lkQ_init(&ls->loader_list); 311 | ls->binpath = lkP_getmodulename(S); 312 | lk_setdata(lk_current(S), ls); 313 | } 314 | else if (sig == NULL) { /* free */ 315 | lk_free(S, ls, sizeof(*ls)); 316 | lk_freelock(ls->lock); 317 | lk_freetable(S, &ls->preloads); 318 | lk_freepool(S, &ls->loader_pool); 319 | lk_deldata(S, ls->binpath); 320 | } 321 | return LK_WEAK; 322 | } 323 | 324 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 325 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 326 | * cc: flags+='-Wextra -O3' input='service_*.c lokilib.c' */ 327 | 328 | -------------------------------------------------------------------------------- /service_log.c: -------------------------------------------------------------------------------- 1 | #define LOKI_MODULE 2 | #include "loki_services.h" 3 | #include "lk_buffer.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #ifdef _WIN32 11 | # include 12 | #else 13 | # include 14 | # include 15 | # include 16 | # include 17 | #endif 18 | 19 | #ifndef LK_DEFAULT_LOGPATH 20 | # define LK_DEFAULT_LOGPATH "logs/%Y-%M/%S_%Y-%M-%D-%I.log" 21 | #endif 22 | #define LK_MAX_CONFIGNAME 64 23 | #define LK_MAX_CONFIGPATH 256 24 | 25 | typedef struct lk_LogHeader { 26 | const char *level; 27 | const char *service; 28 | const char *tag; 29 | const char *key; 30 | const char *msg; 31 | size_t msglen; 32 | struct tm tm; 33 | lk_Buffer buff; 34 | } lk_LogHeader; 35 | 36 | typedef enum lk_ConfigMaskFlag { 37 | lk_Mreload = 1 << 0, 38 | lk_Mcolor = 1 << 1, 39 | lk_Minterval = 1 << 2, 40 | lk_Mfilepath = 1 << 3 41 | } lk_ConfigMaskFlag; 42 | 43 | typedef struct lk_Dumper { 44 | char name[LK_MAX_CONFIGPATH]; 45 | unsigned index : 8; 46 | unsigned interval : 24; 47 | time_t next_update; 48 | FILE *fp; 49 | } lk_Dumper; 50 | 51 | typedef struct lk_LogConfig { 52 | char name[LK_MAX_CONFIGNAME]; 53 | char filepath[LK_MAX_CONFIGPATH]; 54 | unsigned mask : 8; 55 | unsigned color : 8; /* 1248:RGBI, low 4bit: fg, high 4bit: bg */ 56 | unsigned interval : 24; 57 | lk_Dumper *dumper; 58 | } lk_LogConfig; 59 | 60 | typedef struct lk_LogState { 61 | lk_State *S; 62 | lk_Table config; 63 | lk_Table dump; 64 | lk_MemPool configs; 65 | lk_MemPool dumpers; 66 | } lk_LogState; 67 | 68 | #define lkX_readinteger(ls, B, config, key) do { \ 69 | char *s; \ 70 | lk_resetbuffer(B); \ 71 | lk_addfstring(B, "log.%s." #key, config->name); \ 72 | config->mask &= ~lk_M##key; \ 73 | if ((s = lk_getconfig(ls->S, lk_buffer(B)))) { \ 74 | config->key = atoi(s); \ 75 | config->mask |= lk_M##key; \ 76 | lk_deldata(ls->S, (lk_Data*)s); } } while(0) 77 | 78 | #define lkX_readstring(ls, B, config, key) do { \ 79 | char *s; \ 80 | lk_resetbuffer(B); \ 81 | lk_addfstring(B, "log.%s." #key, config->name); \ 82 | config->mask &= ~lk_M##key; \ 83 | if ((s = lk_getconfig(ls->S, lk_buffer(B)))) { \ 84 | lk_strcpy(config->key, lk_buffer(B), \ 85 | LK_MAX_CONFIGPATH); \ 86 | config->mask |= lk_M##key; \ 87 | lk_deldata(ls->S, (lk_Data*)s); } } while(0) 88 | 89 | 90 | /* config dumper */ 91 | 92 | static void lkX_localtime (time_t t, struct tm *tm) 93 | #ifdef _MSC_VER 94 | { localtime_s(tm, &t); } 95 | #elif _POSIX_SOURCE 96 | { localtime_r(&t, tm); } 97 | #else 98 | { *tm = *localtime(&t); } 99 | #endif 100 | 101 | static void lkX_settime (lk_Dumper* dumper, int interval) { 102 | struct tm tm; 103 | time_t now = time(NULL), daytm; 104 | lkX_localtime(now, &tm); 105 | tm.tm_hour = 0; 106 | tm.tm_min = 0; 107 | tm.tm_sec = 0; 108 | daytm = mktime(&tm); 109 | dumper->interval = interval; 110 | dumper->index = (int)((now - daytm) / interval); 111 | dumper->next_update = daytm + (dumper->index + 1) * interval; 112 | } 113 | 114 | static void lkX_createdirs (lk_State *S, const char *path) { 115 | const char *i, *last; 116 | lk_Buffer B; 117 | lk_initbuffer(S, &B); 118 | for (i = last = path; i != '\0'; last = ++i) { 119 | while (*i != '\0' && *i != '/' && *i != '\\') 120 | lk_addchar(&B, *i++); 121 | if (*i == '\0') break; 122 | switch (last - i) { 123 | case 0: continue; 124 | case 1: if (*last == '.') continue; break; 125 | case 2: if (last[0] == '.' && last[1] == '.') continue; break; 126 | } 127 | lk_addchar(&B, '/'); 128 | *lk_prepbuffsize(&B, 1) = '\0'; 129 | #if _WIN32 130 | if (!CreateDirectoryA(lk_buffer(&B), NULL) 131 | && GetLastError() != ERROR_ALREADY_EXISTS) 132 | break; 133 | #else 134 | if (mkdir(lk_buffer(&B), 0777) < 0 && errno != EEXIST) 135 | break; 136 | #endif 137 | } 138 | lk_freebuffer(&B); 139 | } 140 | 141 | static void lkX_escapefn (lk_Buffer *B, const char *s, int idx) { 142 | struct tm tm; 143 | lkX_localtime(time(NULL), &tm); 144 | for (; *s != '\0'; ++s) { 145 | if (*s != '%') { 146 | lk_addchar(B, *s); 147 | continue; 148 | } 149 | switch (*++s) { 150 | case 'Y': lk_addfstring(B, "%04d", tm.tm_year + 1900); break; 151 | case 'M': lk_addfstring(B, "%02d", tm.tm_mon + 1); break; 152 | case 'D': lk_addfstring(B, "%02d", tm.tm_mday); break; 153 | case 'I': lk_addfstring(B, "%d", idx); break; 154 | case '\0': lk_addchar(B, '%'); --s; break; 155 | default: lk_addchar(B, '%'); /* FALLTHROUGH */ 156 | case '%': lk_addchar(B, *s); break; 157 | } 158 | } 159 | lk_addchar(B, '\0'); 160 | } 161 | 162 | static void lkX_openfile (lk_LogState *ls, lk_Dumper* dumper) { 163 | lk_Buffer B; 164 | lk_initbuffer(ls->S, &B); 165 | lkX_escapefn(&B, dumper->name, dumper->index); 166 | lkX_createdirs(ls->S, lk_buffer(&B)); 167 | #ifdef _MSC_VER 168 | fopen_s(&dumper->fp, lk_buffer(&B), "a"); 169 | #else 170 | dumper->fp = fopen(lk_buffer(&B), "a"); 171 | #endif 172 | lk_freebuffer(&B); 173 | } 174 | 175 | static lk_Dumper *lkX_newdumper (lk_LogState *ls, lk_LogConfig *config, lk_LogHeader *hs) { 176 | const char *s; 177 | lk_Buffer B; 178 | lk_Dumper *dumper; 179 | lk_Entry *e; 180 | if (!(config->mask & lk_Mfilepath)) 181 | return NULL; 182 | lk_initbuffer(ls->S, &B); 183 | for (s = config->filepath; *s != '\0'; ++s) { 184 | if (*s != '%') { 185 | lk_addchar(&B, *s); 186 | continue; 187 | } 188 | switch (*++s) { 189 | case 'L': lk_addstring(&B, hs->level); break; 190 | case 'S': lk_addstring(&B, hs->service); break; 191 | case 'T': lk_addstring(&B, hs->tag); break; 192 | default: lk_addchar(&B, '%'); 193 | lk_addchar(&B, *s); break; 194 | } 195 | } 196 | lk_addchar(&B, '\0'); 197 | e = lk_settable(ls->S, &ls->dump, lk_buffer(&B)); 198 | if (e->key != lk_buffer(&B)) return (lk_Dumper*)e->key; 199 | dumper = (lk_Dumper*)lk_poolalloc(ls->S, &ls->dumpers); 200 | memset(dumper, 0, sizeof(*dumper)); 201 | lk_strcpy(dumper->name, lk_buffer(&B), LK_MAX_CONFIGPATH); 202 | lk_freebuffer(&B); 203 | if (config->interval > 0) 204 | lkX_settime(dumper, config->interval); 205 | lkX_openfile(ls, dumper); 206 | e->key = dumper->name; 207 | return dumper; 208 | } 209 | 210 | static int lkX_wheelfile (lk_LogState* ls, lk_Dumper* dumper) { 211 | if (time(NULL) > dumper->next_update) { 212 | if (dumper->fp) fclose(dumper->fp); 213 | lkX_settime(dumper, dumper->interval); 214 | lkX_openfile(ls, dumper); 215 | } 216 | return 0; 217 | } 218 | 219 | 220 | /* config reader */ 221 | 222 | static lk_LogConfig *lkX_newconfig (lk_LogState *ls, const char *name) { 223 | lk_Entry *e = lk_settable(ls->S, &ls->config, name); 224 | lk_LogConfig *config = (lk_LogConfig*)e->key; 225 | if (e->key != name) return config; 226 | config = (lk_LogConfig*)lk_poolalloc(ls->S, &ls->configs); 227 | memset(config, 0, sizeof(*config)); 228 | lk_strcpy(config->name, name, LK_MAX_CONFIGNAME); 229 | config->mask |= lk_Mreload; 230 | config->color = 0x77; 231 | e->key = config->name; 232 | return config; 233 | } 234 | 235 | static lk_LogConfig* lkX_getconfig (lk_LogState *ls, const char *name) { 236 | lk_LogConfig *config = lkX_newconfig(ls, name); 237 | if (config->mask & lk_Mreload) { 238 | lk_Buffer B; 239 | lk_initbuffer(ls->S, &B); 240 | lkX_readinteger(ls, &B, config, color); 241 | lkX_readinteger(ls, &B, config, interval); 242 | lkX_readstring(ls, &B, config, filepath); 243 | if (config->interval > 60 * 60 * 24) 244 | config->interval = 60 * 60 * 24; 245 | lk_freebuffer(&B); 246 | } 247 | return config; 248 | } 249 | 250 | static int lkX_mergeconfig (lk_LogConfig *c1, lk_LogConfig *c2) { 251 | if (c1 == NULL || c2 == NULL) return -1; 252 | if (c2->mask & lk_Mcolor) c1->color = c2->color; 253 | if (c2->mask & lk_Minterval) c1->interval = c2->interval; 254 | if (c2->mask & lk_Mfilepath) lk_strcpy(c1->filepath, c2->filepath, LK_MAX_CONFIGPATH); 255 | c1->mask |= c2->mask; 256 | return 0; 257 | } 258 | 259 | static lk_LogConfig* lkX_setconfig (lk_LogState *ls, lk_LogHeader *hs) { 260 | lk_LogConfig *config = lkX_getconfig(ls, hs->key); 261 | if (config->mask & lk_Mreload) { 262 | lk_LogConfig *other = lkX_getconfig(ls, hs->level); 263 | if (other->mask != lk_Mreload) 264 | lkX_mergeconfig(config, other); 265 | other = lkX_getconfig(ls, hs->service); 266 | if (other->mask == lk_Mreload) 267 | other = lkX_getconfig(ls, "default_service"); 268 | if (other->mask != lk_Mreload) 269 | lkX_mergeconfig(config, other); 270 | if (hs->tag) { 271 | other = lkX_getconfig(ls, hs->tag); 272 | if (other->mask != lk_Mreload) 273 | lkX_mergeconfig(config, other); 274 | } 275 | if ((config->mask & lk_Mfilepath) && config->dumper == NULL) 276 | config->dumper = lkX_newdumper(ls, config, hs); 277 | config->mask &= ~lk_Mreload; 278 | } 279 | return config; 280 | } 281 | 282 | 283 | /* config parser */ 284 | 285 | static void lkX_parseheader (lk_LogHeader *hs, const char* service, const char* s, size_t len) { 286 | const char *end = s + len; 287 | size_t offset_key = 0; /* key struct: [level][service][tag] */ 288 | hs->level = "info"; 289 | hs->service = service; 290 | hs->tag = NULL; 291 | hs->msg = s; 292 | if (len >= 3 && s[1] == '[') { 293 | const char *start = s + 2; 294 | switch (*s) { 295 | default: goto no_tag; 296 | case 'I': break; 297 | case 'T': hs->level = "trace"; break; 298 | case 'V': hs->level = "verbose"; break; 299 | case 'W': hs->level = "warning"; break; 300 | case 'E': hs->level = "error"; break; 301 | } 302 | for (s = start; s < end && *s != ']'; ++s) 303 | ; 304 | if (s == end) goto no_tag; 305 | if (s - start != 0) { 306 | lk_addlstring(&hs->buff, start, s - start); 307 | lk_addchar(&hs->buff, '\0'); 308 | offset_key = lk_buffsize(&hs->buff); 309 | } 310 | hs->msg = *++s == ' ' ? s + 1 : s; 311 | } 312 | no_tag: 313 | hs->msglen = end - hs->msg; 314 | lk_addfstring(&hs->buff, "[%s][%s][", hs->level, service); 315 | if (offset_key != 0) { 316 | lk_prepbuffsize(&hs->buff, offset_key-1); 317 | lk_addlstring(&hs->buff, lk_buffer(&hs->buff), offset_key-1); 318 | hs->tag = lk_buffer(&hs->buff); 319 | } 320 | lk_addlstring(&hs->buff, "]\0", 2); 321 | hs->key = lk_buffer(&hs->buff) + offset_key; 322 | } 323 | 324 | static void lkX_headerdump (lk_LogState *ls, lk_LogHeader *hs, FILE *fp) { 325 | struct tm *tm = &hs->tm; 326 | (void)ls; 327 | if (hs->tag) fprintf(fp, "[%c][%s][%02d:%02d:%02d][%s]: ", 328 | toupper(hs->level[0]), hs->service, 329 | tm->tm_hour, tm->tm_min, tm->tm_sec, hs->tag); 330 | else fprintf(fp, "[%c][%s][%02d:%02d:%02d]: ", 331 | toupper(hs->level[0]), hs->service, 332 | tm->tm_hour, tm->tm_min, tm->tm_sec); 333 | } 334 | 335 | static void lkX_filedump (lk_LogState *ls, lk_LogHeader *hs, lk_Dumper *dumper) { 336 | if (dumper->interval > 0 || !dumper->fp) 337 | lkX_wheelfile(ls, dumper); 338 | if (dumper->fp) { 339 | lkX_headerdump(ls, hs, dumper->fp); 340 | fwrite(hs->msg, 1, hs->msglen, dumper->fp); 341 | fputc('\n', dumper->fp); 342 | fflush(dumper->fp); 343 | } 344 | } 345 | 346 | static void lkX_screendump (lk_LogState *ls, const char *s, size_t len, int color) { 347 | #ifdef _WIN32 348 | lk_Buffer B; 349 | HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); 350 | WORD attr = 0, reset = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; 351 | DWORD written; 352 | LPWSTR buff = NULL; 353 | int bytes, cp = CP_UTF8; 354 | char eol = '\n'; 355 | if (color & 0x01) attr |= FOREGROUND_RED; 356 | if (color & 0x02) attr |= FOREGROUND_GREEN; 357 | if (color & 0x04) attr |= FOREGROUND_BLUE; 358 | if (color & 0x08) attr |= FOREGROUND_INTENSITY; 359 | if (color & 0x10) attr |= BACKGROUND_RED; 360 | if (color & 0x20) attr |= BACKGROUND_GREEN; 361 | if (color & 0x40) attr |= BACKGROUND_BLUE; 362 | if (color & 0x80) attr |= BACKGROUND_INTENSITY; 363 | bytes = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, (int)len, NULL, 0); 364 | lk_initbuffer(ls->S, &B); 365 | if (bytes != 0) { 366 | buff = (LPWSTR)lk_prepbuffsize(&B, bytes * sizeof(WCHAR)); 367 | bytes = MultiByteToWideChar(cp, 0, s, (int)len, buff, bytes); 368 | } 369 | SetConsoleTextAttribute(h, attr); 370 | if (buff) WriteConsoleW(h, buff, bytes, &written, NULL); 371 | else WriteConsoleA(h, s, (DWORD)len, &written, NULL); 372 | SetConsoleTextAttribute(h, reset); 373 | lk_freebuffer(&B); 374 | WriteConsoleA(h, &eol, 1, &written, NULL); 375 | #else 376 | int fg = 30 + (color & 0x7), bg = 40 + ((color & 0x70)>>4); 377 | (void)ls; 378 | if (color & 0x08) fg += 60; 379 | if (color & 0x80) bg += 60; 380 | fprintf(stdout, "\x1B[%d;%dm%.*s\x1B[0m\n", fg, bg, (int)len, s); 381 | fflush(stdout); 382 | #endif 383 | } 384 | 385 | static int lkX_writelog (lk_LogState *ls, const char* service_name, const char* s, size_t len) { 386 | lk_LogConfig *config; 387 | lk_LogHeader hs; 388 | lk_initbuffer(ls->S, &hs.buff); 389 | lkX_parseheader(&hs, service_name, s, len); 390 | config = lkX_setconfig(ls, &hs); 391 | lkX_localtime(time(NULL), &hs.tm); 392 | if (config->dumper) 393 | lkX_filedump(ls, &hs, config->dumper); 394 | if (config->color != 0) { 395 | lkX_headerdump(ls, &hs, stdout); 396 | lkX_screendump(ls, hs.msg, hs.msglen, config->color); 397 | } 398 | lk_freebuffer(&hs.buff); 399 | return 0; 400 | } 401 | 402 | 403 | /* config initialize */ 404 | 405 | static lk_LogState *lkX_newstate (lk_State *S) { 406 | lk_LogState *ls = (lk_LogState*)lk_malloc(S, sizeof(lk_LogState)); 407 | lk_LogConfig *config; 408 | ls->S = S; 409 | lk_inittable(&ls->config, sizeof(lk_Entry)); 410 | lk_inittable(&ls->dump, sizeof(lk_Entry)); 411 | lk_initpool(&ls->configs, sizeof(lk_LogConfig)); 412 | lk_initpool(&ls->dumpers, sizeof(lk_Dumper)); 413 | 414 | /* initialize config */ 415 | lk_resizetable(S, &ls->config, 32); 416 | config = lkX_newconfig(ls, "info"); 417 | config->color = 0x07; 418 | config->mask = lk_Mcolor; 419 | config = lkX_newconfig(ls, "trace"); 420 | config->color = 0x0F; 421 | config->mask = lk_Mcolor; 422 | config = lkX_newconfig(ls, "verbose"); 423 | config->color = 0x70; 424 | config->mask = lk_Mcolor; 425 | config = lkX_newconfig(ls, "warning"); 426 | config->color = 0x0B; 427 | config->mask = lk_Mcolor; 428 | config = lkX_newconfig(ls, "error"); 429 | config->color = 0x9F; 430 | config->mask = lk_Mcolor; 431 | config = lkX_newconfig(ls, "default_service"); 432 | lk_strcpy(config->filepath, LK_DEFAULT_LOGPATH, LK_MAX_CONFIGPATH); 433 | config->interval = 3600; 434 | config->mask = lk_Mfilepath|lk_Minterval; 435 | 436 | return ls; 437 | } 438 | 439 | static void lkX_delstate (lk_LogState* ls) { 440 | lk_Entry *e = NULL; 441 | while (lk_nextentry(&ls->config, &e)) { 442 | lk_LogConfig *config = (lk_LogConfig*)e->key; 443 | lk_poolfree(&ls->configs, config); 444 | } 445 | while (lk_nextentry(&ls->dump, &e)) { 446 | lk_Dumper *dumper = (lk_Dumper*)e->key; 447 | if (dumper && dumper->fp) fclose(dumper->fp); 448 | lk_poolfree(&ls->dumpers, dumper); 449 | } 450 | lk_freetable(ls->S, &ls->config); 451 | lk_freetable(ls->S, &ls->dump); 452 | lk_freepool(ls->S, &ls->configs); 453 | lk_freepool(ls->S, &ls->dumpers); 454 | lk_free(ls->S, ls, sizeof(lk_LogState)); 455 | } 456 | 457 | static int lkX_update (lk_State *S, lk_Slot *slot, lk_Signal *sig) { 458 | lk_LogState *ls = (lk_LogState*)lk_data(slot); 459 | lk_Entry *e = NULL; 460 | (void)S, (void)sig; 461 | while (lk_nextentry(&ls->config, &e)) { 462 | lk_LogConfig *config = (lk_LogConfig*)e->key; 463 | config->mask |= lk_Mreload; 464 | config->dumper = NULL; 465 | } 466 | while (lk_nextentry(&ls->dump, &e)) { 467 | lk_Dumper *dumper = (lk_Dumper*)e->key; 468 | if (dumper && dumper->fp) fclose(dumper->fp); 469 | lk_poolfree(&ls->dumpers, dumper); 470 | e->key = NULL; 471 | } 472 | return LK_OK; 473 | } 474 | 475 | static int lkX_launch (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 476 | lk_LogState *ls = (lk_LogState*)lk_data(lk_current(S)); 477 | const char *msg = (const char*)sig->data; 478 | lk_Data *data = lk_newfstring(S, "V[] service '%s'(%p) launched", msg, msg); 479 | lkX_writelog(ls, (const char*)sender, (const char*)data, lk_len(data)); 480 | lk_deldata(S, data); 481 | return LK_OK; 482 | } 483 | 484 | static int lkX_close (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 485 | lk_LogState *ls = (lk_LogState*)lk_data(lk_current(S)); 486 | const char *msg = (const char*)sig->data; 487 | lk_Data *data = lk_newfstring(S, "V[] service '%s'(%p) closed", msg, msg); 488 | lkX_writelog(ls, (const char*)sender, (const char*)data, lk_len(data)); 489 | lk_deldata(S, data); 490 | return LK_OK; 491 | } 492 | 493 | LKMOD_API int loki_service_log (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 494 | lk_LogState *ls = (lk_LogState*)lk_userdata(S); 495 | if (sender == NULL) { 496 | ls = lkX_newstate(S); 497 | lk_newslot(S, "update", lkX_update, ls); 498 | lk_newslot(S, LK_SLOTNAME_LAUNCH, lkX_launch, ls); 499 | lk_newslot(S, LK_SLOTNAME_CLOSE, lkX_close, ls); 500 | lk_setdata(lk_current(S), ls); 501 | return LK_WEAK; 502 | } 503 | else if (sig == NULL) { 504 | const char *msg = (const char*)lk_self(S); 505 | lk_Data *data = lk_newfstring(S, "V[] service '%s'(%p) closed", msg, msg); 506 | lkX_writelog(ls, (const char*)sender, (const char*)data, lk_len(data)); 507 | lk_deldata(S, data); 508 | lkX_delstate(ls); 509 | } 510 | else { 511 | const char *msg = (const char*)sig->data; 512 | size_t len = sig->isdata ? lk_len((lk_Data*)sig->data) : strlen(msg); 513 | lkX_writelog(ls, (const char*)sender, msg, len); 514 | } 515 | return LK_OK; 516 | } 517 | 518 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 519 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 520 | * cc: flags+='-Wextra -O3' input='service_*.c lokilib.c' */ 521 | 522 | -------------------------------------------------------------------------------- /service_socket.c: -------------------------------------------------------------------------------- 1 | #define LOKI_MODULE 2 | #include "loki_services.h" 3 | 4 | #define ZN_IMPLEMENTATION 5 | #include "znet/znet.h" 6 | #include "znet/zn_buffer.h" 7 | 8 | 9 | #define lkX_state(S) ((lk_ZNetState*)lk_userdata(S)) 10 | 11 | #define lkX_getpooled(var,type) type *var; do { \ 12 | lk_lock(zs->lock); \ 13 | var = (type*)lk_poolalloc(zs->S, &zs->var##s);\ 14 | memset(var, 0, sizeof(*var)); \ 15 | var->zs = zs; \ 16 | lk_unlock(zs->lock); } while (0) 17 | 18 | #define lkX_putpooled(var) do { \ 19 | lk_lock(zs->lock); \ 20 | lk_poolfree(&zs->var##s, var); \ 21 | lk_unlock(zs->lock); } while (0) 22 | 23 | #define lkX_getcached(var,type) type *var; do { \ 24 | lk_lock(zs->lock); \ 25 | if ((var = zs->freed_##var##s) != NULL) \ 26 | znL_remove(var); \ 27 | else { \ 28 | var = (type*)lk_malloc(zs->S, sizeof(type)); \ 29 | memset(var, 0, sizeof(*var)); \ 30 | var->zs = zs; } \ 31 | znL_insert(&zs->var##s, var); \ 32 | lk_unlock(zs->lock); } while (0) 33 | 34 | #define lkX_putcached(var) do { \ 35 | lk_lock(zs->lock); \ 36 | znL_remove(var); \ 37 | znL_insert(&zs->freed_##var##s, var); \ 38 | lk_unlock(zs->lock); } while (0) 39 | 40 | typedef struct lk_ZNetState { 41 | lk_Slot *poll; 42 | lk_State *S; 43 | zn_State *zs; 44 | unsigned closing; 45 | lk_Table handler_map; 46 | lk_MemPool cmds; 47 | lk_MemPool accepts; 48 | lk_MemPool handlers; 49 | lk_Tcp *tcps, *freed_tcps; 50 | lk_Udp *udps, *freed_udps; 51 | lk_Lock lock; /* lock of freed */ 52 | } lk_ZNetState; 53 | 54 | typedef struct lk_RecvHandlers { 55 | lk_HeaderHandler *on_header; void *ud_header; 56 | lk_PacketHandler *on_packet; void *ud_packet; 57 | lk_RecvFromHandler *on_recvfrom; void *ud_recvfrom; 58 | } lk_RecvHandlers; 59 | 60 | typedef enum lk_PostCmdType { 61 | LK_CMD_ACCEPT_DELETE, 62 | LK_CMD_ACCEPT_LISTEN, 63 | LK_CMD_TCP_DELETE, 64 | LK_CMD_TCP_CONNECT, 65 | LK_CMD_TCP_SEND, 66 | LK_CMD_TCP_RECV, 67 | LK_CMD_UDP_BIND, 68 | LK_CMD_UDP_DELETE, 69 | LK_CMD_UDP_SENDTO, 70 | LK_CMD_UDP_RECVFROM, 71 | LK_CMD_COUNT 72 | } lk_PostCmdType; 73 | 74 | typedef enum lk_SignalType { 75 | LK_SIGTYPE_ACCEPT_LISTEN, 76 | LK_SIGTYPE_TCP_CONNECT, 77 | LK_SIGTYPE_TCP_RECV, 78 | LK_SIGTYPE_UDP_BIND, 79 | LK_SIGTYPE_UDP_RECVFROM, 80 | LK_SIGTYPE_COUNT 81 | } lk_SignalType; 82 | 83 | typedef struct lk_PostCmd { 84 | lk_ZNetState *zs; 85 | lk_Service *service; 86 | void *data; 87 | union { 88 | lk_Accept *accept; 89 | lk_Tcp *tcp; 90 | lk_Udp *udp; 91 | } u; 92 | union { 93 | lk_AcceptHandler *on_accept; 94 | lk_ConnectHandler *on_connect; 95 | lk_UdpBindHandler *on_udpbind; 96 | } h; 97 | lk_PostCmdType cmd; 98 | unsigned error; 99 | zn_PeerInfo info; 100 | } lk_PostCmd; 101 | 102 | struct lk_Accept { 103 | lk_ZNetState *zs; 104 | lk_Service *service; 105 | lk_AcceptHandler *handler; void *ud; 106 | zn_Accept *accept; 107 | lk_Tcp *tcp; 108 | unsigned error; 109 | }; 110 | 111 | struct lk_Tcp { 112 | znL_entry(lk_Tcp); 113 | lk_ZNetState *zs; 114 | lk_Service *service; 115 | lk_RecvHandlers *handlers; 116 | zn_Tcp *tcp; 117 | void *data; 118 | zn_SendBuffer send; 119 | zn_RecvBuffer recv; 120 | zn_PeerInfo info; 121 | unsigned count : 24; 122 | unsigned error : 7; 123 | unsigned closing : 1; 124 | }; 125 | 126 | struct lk_Udp { 127 | znL_entry(lk_Udp); 128 | lk_ZNetState *zs; 129 | lk_Service *service; 130 | lk_RecvHandlers *handlers; 131 | zn_Udp *udp; 132 | zn_Buffer buff; 133 | zn_PeerInfo info; 134 | unsigned count : 24; 135 | unsigned error : 8; 136 | }; 137 | 138 | typedef struct lk_HandlersEntry { 139 | lk_Entry entry; 140 | lk_RecvHandlers *handlers; 141 | } lk_HandlersEntry; 142 | 143 | static lk_ZNetState *lkX_newstate (lk_State *S) { 144 | lk_ZNetState *zs = (lk_ZNetState*)lk_malloc(S, sizeof(lk_ZNetState)); 145 | memset(zs, 0, sizeof(*zs)); 146 | zs->S = S; 147 | zn_initialize(); 148 | if (!lk_initlock(&zs->lock)) goto err_lock; 149 | if ((zs->zs = zn_newstate()) == NULL) goto err_znet; 150 | lk_inittable(&zs->handler_map, sizeof(lk_HandlersEntry)); 151 | lk_initpool(&zs->cmds, sizeof(lk_PostCmd)); 152 | lk_initpool(&zs->accepts, sizeof(lk_Accept)); 153 | lk_initpool(&zs->handlers, sizeof(lk_RecvHandlers)); 154 | return zs; 155 | err_znet: 156 | lk_freelock(zs->lock); 157 | err_lock: 158 | lk_free(S, zs, sizeof(lk_ZNetState)); 159 | lk_discard(S); 160 | return NULL; 161 | } 162 | 163 | static lk_RecvHandlers *lkX_gethandlers (lk_ZNetState *zs, lk_Service *svr) { 164 | lk_HandlersEntry *e; 165 | lk_RecvHandlers *hs = NULL; 166 | lk_lock(zs->lock); 167 | e = (lk_HandlersEntry*)lk_gettable(&zs->handler_map, (const char*)svr); 168 | if (e) hs = (lk_RecvHandlers*)e->handlers; 169 | lk_unlock(zs->lock); 170 | return hs; 171 | } 172 | 173 | static void lkX_copyinfo (lk_PostCmd *cmd, const char *addr, unsigned port) { 174 | lk_strcpy(cmd->info.addr, addr, ZN_MAX_ADDRLEN); 175 | cmd->info.port = port; 176 | } 177 | 178 | static int lkX_poller (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 179 | lk_ZNetState *zs = lkX_state(S); 180 | (void)sender, (void)sig; 181 | while (!zs->closing) 182 | zn_run(zs->zs, ZN_RUN_LOOP); 183 | lk_free(S, zs, sizeof(lk_ZNetState)); 184 | return LK_OK; 185 | } 186 | 187 | static void lkX_postdeletor (void *ud, zn_State *S) { 188 | lk_ZNetState *zs = (lk_ZNetState*)ud; 189 | lk_PtrEntry *e = NULL; 190 | (void)S; 191 | zs->closing = 1; 192 | zn_close(zs->zs); 193 | zn_deinitialize(); 194 | while (lk_nextentry(&zs->handler_map, (lk_Entry**)&e)) 195 | if (e->data) lk_poolfree(&zs->handlers, e->data); 196 | lk_freetable(zs->S, &zs->handler_map); 197 | lk_freepool(zs->S, &zs->cmds); 198 | lk_freepool(zs->S, &zs->accepts); 199 | lk_freepool(zs->S, &zs->handlers); 200 | znL_apply(lk_Tcp, &zs->tcps, lk_free(zs->S, cur, sizeof(lk_Tcp))); 201 | znL_apply(lk_Tcp, &zs->freed_tcps, lk_free(zs->S, cur, sizeof(lk_Tcp))); 202 | znL_apply(lk_Udp, &zs->udps, lk_free(zs->S, cur, sizeof(lk_Udp))); 203 | znL_apply(lk_Udp, &zs->freed_udps, lk_free(zs->S, cur, sizeof(lk_Udp))); 204 | } 205 | 206 | 207 | /* post worker */ 208 | 209 | static zn_RecvHandler lkX_onrecv; 210 | static zn_RecvFromHandler lkX_onrecvfrom; 211 | 212 | static size_t lkX_onheader (void *ud, const char *buff, size_t size) { 213 | lk_Tcp *tcp = (lk_Tcp*)ud; 214 | lk_RecvHandlers *h = tcp->handlers; 215 | if (h && h->on_header) 216 | return h->on_header(tcp->zs->S, h->ud_header, tcp, buff, size); 217 | return size; 218 | } 219 | 220 | static void lkX_onpacket (void *ud, const char *buff, size_t size) { 221 | lk_Tcp *tcp = (lk_Tcp*)ud; 222 | lk_RecvHandlers *h = tcp->handlers; 223 | if (h && h->on_packet) 224 | h->on_packet(tcp->zs->S, h->ud_header, tcp, buff, size); 225 | } 226 | 227 | static lk_Tcp *lkX_preparetcp (lk_ZNetState *zs, lk_Service *svr, zn_Tcp *ztcp) { 228 | int ret; 229 | lkX_getcached(tcp, lk_Tcp); 230 | tcp->service = svr; 231 | tcp->handlers = lkX_gethandlers(zs, svr); 232 | zn_initrecvbuffer(&tcp->recv); 233 | zn_initsendbuffer(&tcp->send); 234 | zn_recvonheader(&tcp->recv, lkX_onheader, tcp); 235 | zn_recvonpacket(&tcp->recv, lkX_onpacket, tcp); 236 | ret = zn_recv(ztcp, zn_recvbuff(&tcp->recv), zn_recvsize(&tcp->recv), 237 | lkX_onrecv, tcp); 238 | if (ret != ZN_OK) { 239 | zn_PeerInfo info; 240 | zn_getpeerinfo(ztcp, &info); 241 | lk_log(zs->S, "E[recv]" lk_loc("[%p] %s (%s:%d)"), 242 | tcp, zn_strerror(ret), info.addr, info.port); 243 | zn_deltcp(ztcp); 244 | lkX_putcached(tcp); 245 | return NULL; 246 | } 247 | tcp->tcp = ztcp; 248 | return tcp; 249 | } 250 | 251 | static lk_Udp *lkX_prepareudp (lk_ZNetState *zs, lk_Service *svr, zn_Udp *zudp) { 252 | int ret; 253 | lkX_getcached(udp, lk_Udp); 254 | udp->service = svr; 255 | udp->handlers = lkX_gethandlers(zs, svr); 256 | zn_initbuffer(&udp->buff); 257 | ret = zn_recvfrom(zudp, 258 | zn_buffer(&udp->buff), (unsigned)zn_bufflen(&udp->buff), 259 | lkX_onrecvfrom, udp); 260 | if (ret != ZN_OK) { 261 | lk_log(zs->S, "E[recvfrom]" lk_loc("[%p] %s"), udp, zn_strerror(ret)); 262 | zn_deludp(zudp); 263 | lkX_putcached(udp); 264 | return NULL; 265 | } 266 | udp->udp = zudp; 267 | return udp; 268 | } 269 | 270 | static void lkX_deltcp (lk_Tcp *tcp) { 271 | lk_Signal sig = LK_RESPONSE; 272 | if (tcp->tcp) { 273 | zn_deltcp(tcp->tcp); 274 | zn_resetrecvbuffer(&tcp->recv); 275 | zn_resetsendbuffer(&tcp->send); 276 | tcp->tcp = NULL; 277 | } 278 | sig.type = LK_SIGTYPE_TCP_RECV; 279 | sig.data = tcp; 280 | tcp->error = ZN_ERROR; 281 | lk_emit((lk_Slot*)tcp->service, &sig); 282 | } 283 | 284 | static void lkX_accepterror (lk_Accept *accept, unsigned err) { 285 | lk_Signal sig = LK_RESPONSE; 286 | sig.type = LK_SIGTYPE_ACCEPT_LISTEN; 287 | sig.data = accept; 288 | accept->error = err; 289 | lk_emit((lk_Slot*)accept->service, &sig); 290 | } 291 | 292 | static void lkX_onaccept (void *ud, zn_Accept *zaccept, unsigned err, zn_Tcp *ztcp) { 293 | lk_Accept *accept = (lk_Accept*)ud; 294 | lk_ZNetState *zs = accept->zs; 295 | lk_Signal sig = LK_RESPONSE; 296 | sig.type = LK_SIGTYPE_ACCEPT_LISTEN; 297 | sig.data = accept; 298 | accept->error = err; 299 | if (err == ZN_OK && (accept->tcp = 300 | lkX_preparetcp(zs, accept->service, ztcp)) == NULL) 301 | return; 302 | if (err == ZN_OK && 303 | (err = zn_accept(zaccept, lkX_onaccept, accept)) == ZN_OK) 304 | lk_log(zs->S, "I[accept]" lk_loc("[%p][%p] new connection accepted"), 305 | accept, accept->tcp); 306 | else { 307 | lk_log(zs->S, "E[accept]" lk_loc("[%p] %s"), accept, zn_strerror(err)); 308 | zn_delaccept(zaccept); 309 | accept->accept = NULL; 310 | } 311 | lk_emit((lk_Slot*)accept->service, &sig); 312 | } 313 | 314 | static void lkX_onconnect (void *ud, zn_Tcp *ztcp, unsigned err) { 315 | lk_PostCmd *cmd = (lk_PostCmd*)ud; 316 | lk_ZNetState *zs = cmd->zs; 317 | lk_Signal sig = LK_RESPONSE; 318 | sig.type = LK_SIGTYPE_TCP_CONNECT; 319 | sig.data = cmd; 320 | cmd->error = err; 321 | if (err == ZN_OK) { 322 | cmd->u.tcp = lkX_preparetcp(zs, cmd->service, ztcp); 323 | lk_log(zs->S, "I[connect]" lk_loc("[%p] %s:%d connected"), 324 | cmd->u.tcp, cmd->info.addr, cmd->info.port); 325 | } 326 | else { 327 | zn_deltcp(ztcp); 328 | lk_log(zs->S, "E[connect]" lk_loc("[%p] %s (%s:%d)"), 329 | cmd->u.tcp, zn_strerror(err), cmd->info.addr, cmd->info.port); 330 | } 331 | lk_emit((lk_Slot*)cmd->service, &sig); 332 | } 333 | 334 | static void lkX_onrecv (void *ud, zn_Tcp *ztcp, unsigned err, unsigned count) { 335 | lk_Tcp *tcp = (lk_Tcp*)ud; 336 | lk_ZNetState *zs = tcp->zs; 337 | lk_Signal sig = LK_RESPONSE; 338 | sig.type = LK_SIGTYPE_TCP_RECV; 339 | sig.data = tcp; 340 | tcp->error = err; 341 | tcp->count = count; 342 | if (err != ZN_OK) { 343 | lk_log(zs->S, "E[recv]" lk_loc("[%p] %s"), tcp, zn_strerror(err)); 344 | zn_deltcp(ztcp); 345 | tcp->tcp = NULL; 346 | } 347 | lk_emit((lk_Slot*)tcp->service, &sig); 348 | } 349 | 350 | static void lkX_onsend (void *ud, zn_Tcp *ztcp, unsigned err, unsigned count) { 351 | lk_Tcp *tcp = (lk_Tcp*)ud; 352 | lk_ZNetState *zs = tcp->zs; 353 | if (err == ZN_OK) { 354 | if (zn_sendfinish(&tcp->send, count)) 355 | err = zn_send(tcp->tcp, 356 | zn_sendbuff(&tcp->send), (unsigned)zn_sendsize(&tcp->send), 357 | lkX_onsend, ud); 358 | else if (tcp->closing) { 359 | lk_log(zs->S, "I[close]" lk_loc("[%p] tcp closed"), tcp); 360 | zn_deltcp(ztcp); 361 | lkX_putcached(tcp); 362 | } 363 | } 364 | if (err != ZN_OK) { 365 | lk_log(zs->S, "E[send]" lk_loc("[%p] %s"), tcp, zn_strerror(err)); 366 | zn_deltcp(ztcp); 367 | tcp->tcp = NULL; 368 | } 369 | } 370 | 371 | static void lkX_onrecvfrom (void *ud, zn_Udp *zudp, unsigned err, unsigned count, const char *addr, unsigned port) { 372 | lk_Udp *udp = (lk_Udp*)ud; 373 | lk_ZNetState *zs = udp->zs; 374 | lk_Signal sig = LK_RESPONSE; 375 | sig.type = LK_SIGTYPE_UDP_RECVFROM; 376 | sig.data = udp; 377 | udp->error = err; 378 | udp->count = count; 379 | lk_strcpy(udp->info.addr, addr, ZN_MAX_ADDRLEN); 380 | udp->info.port = port; 381 | if (err != ZN_OK) { 382 | lk_log(zs->S, "E[recvfrom]" lk_loc("[%p] %s"), udp, zn_strerror(err)); 383 | zn_deludp(zudp); 384 | udp->udp = NULL; 385 | } 386 | lk_emit((lk_Slot*)udp->service, &sig); 387 | } 388 | 389 | static void lkX_poster (void *ud, zn_State *S) { 390 | lk_PostCmd *cmd = (lk_PostCmd*)ud; 391 | lk_ZNetState *zs = cmd->zs; 392 | (void)S; 393 | switch (cmd->cmd) { 394 | default: break; 395 | case LK_CMD_ACCEPT_DELETE: { 396 | lk_Accept *accept = cmd->u.accept; 397 | if (accept->accept) zn_delaccept(accept->accept); 398 | lk_log(zs->S, "I[close]" lk_loc("[%p] accept closed"), accept); 399 | lkX_putpooled(accept); 400 | } break; 401 | case LK_CMD_TCP_DELETE: { 402 | lk_Tcp *tcp = cmd->u.tcp; 403 | if (tcp->tcp) { 404 | if (zn_bufflen(tcp->send.sending) || zn_bufflen(tcp->send.pending)) 405 | tcp->closing = 1; 406 | else { 407 | lk_log(zs->S, "I[close]" lk_loc("[%p] tcp closed"), tcp); 408 | zn_deltcp(tcp->tcp); 409 | lkX_putcached(tcp); 410 | } 411 | } 412 | } break; 413 | case LK_CMD_UDP_DELETE: { 414 | lk_Udp *udp = cmd->u.udp; 415 | if (udp->udp) zn_deludp(udp->udp); 416 | lk_log(zs->S, "I[close]" lk_loc("[%p] udp closed"), udp); 417 | lkX_putcached(udp); 418 | } break; 419 | case LK_CMD_ACCEPT_LISTEN: { 420 | lk_Accept *accept = cmd->u.accept; 421 | zn_Accept *zaccept = accept->accept; 422 | int ret = ZN_ERROR; 423 | if (zaccept) zn_delaccept(zaccept); 424 | accept->accept = zaccept = zn_newaccept(zs->zs); 425 | if (zaccept != NULL 426 | && (ret = zn_listen(zaccept, cmd->info.addr, cmd->info.port)) == ZN_OK 427 | && (ret = zn_accept(zaccept, lkX_onaccept, accept)) == ZN_OK) 428 | lk_log(zs->S, "I[listen]" lk_loc("[%p] listen (%s:%d)"), 429 | accept, cmd->info.addr, cmd->info.port); 430 | else { 431 | lk_log(zs->S, "E[listen]" lk_loc("[%p] %s (%s:%d)"), 432 | accept, zaccept ? zn_strerror(ret) : "can not create zn_Accept", 433 | cmd->info.addr, cmd->info.port); 434 | if (zaccept) zn_delaccept(zaccept); 435 | lkX_accepterror(accept, ret); 436 | } 437 | } break; 438 | case LK_CMD_TCP_CONNECT: { 439 | zn_Tcp *tcp = zn_newtcp(zs->zs); 440 | int ret = tcp == NULL ? ZN_ERROR : zn_connect(tcp, 441 | cmd->info.addr, cmd->info.port, lkX_onconnect, cmd); 442 | if (ret != ZN_OK) { 443 | lk_Signal sig = LK_RESPONSE; 444 | lk_log(zs->S, "E[connect]" lk_loc("%s (%s:%d)"), 445 | tcp ? zn_strerror(ret) : "can not create zn_Tcp", 446 | cmd->info.addr, cmd->info.port); 447 | if (tcp) zn_deltcp(tcp); 448 | sig.type = LK_SIGTYPE_TCP_CONNECT; 449 | sig.data = cmd; 450 | cmd->error = ret; 451 | lk_emit((lk_Slot*)cmd->service, &sig); 452 | } 453 | return; 454 | } break; 455 | case LK_CMD_TCP_SEND: { 456 | lk_Tcp *tcp = cmd->u.tcp; 457 | int ret = ZN_OK; 458 | size_t size = lk_len((lk_Data*)cmd->data); 459 | if (tcp->tcp && zn_sendprepare(&tcp->send, (char*)cmd->data, size)) 460 | ret = zn_send(tcp->tcp, 461 | zn_sendbuff(&tcp->send), (unsigned)zn_sendsize(&tcp->send), 462 | lkX_onsend, tcp); 463 | lk_deldata(zs->S, (lk_Data*)cmd->data); 464 | if (ret != ZN_OK) { 465 | lk_log(zs->S, "E[send]" lk_loc("[%p] %s"), tcp, zn_strerror(ret)); 466 | lkX_deltcp(tcp); 467 | } 468 | } break; 469 | case LK_CMD_TCP_RECV: { 470 | lk_Tcp *tcp = cmd->u.tcp; 471 | int ret = tcp->tcp ? zn_recv(tcp->tcp, 472 | zn_recvbuff(&tcp->recv), zn_recvsize(&tcp->recv), 473 | lkX_onrecv, tcp) : ZN_OK; 474 | if (ret != ZN_OK) { 475 | lk_log(zs->S, "E[recv]" lk_loc("[%p] %s"), tcp, zn_strerror(ret)); 476 | lkX_deltcp(tcp); 477 | } 478 | } break; 479 | case LK_CMD_UDP_BIND: { 480 | lk_Signal sig = LK_RESPONSE; 481 | zn_Udp *zudp = zn_newudp(zs->zs, cmd->info.addr, cmd->info.port); 482 | if (zudp != NULL) 483 | cmd->u.udp = lkX_prepareudp(zs, cmd->service, zudp); 484 | else 485 | lk_log(zs->S, "E[bindudp]" lk_loc("can not create zn_Udp (%s:%d)"), 486 | cmd->info.addr, cmd->info.port); 487 | sig.type = LK_SIGTYPE_UDP_BIND; 488 | sig.data = cmd; 489 | lk_emit((lk_Slot*)cmd->service, &sig); 490 | return; 491 | } break; 492 | case LK_CMD_UDP_SENDTO: { 493 | lk_Udp *udp = cmd->u.udp; 494 | size_t size = lk_len((lk_Data*)cmd->data); 495 | int ret = udp->udp ? zn_sendto(udp->udp, 496 | (char*)cmd->data, (unsigned)size, 497 | cmd->info.addr, cmd->info.port) : ZN_OK; 498 | lk_deldata(zs->S, (lk_Data*)cmd->data); 499 | if (ret != ZN_OK) { 500 | lk_log(zs->S, "W[sendto]" lk_loc("[%p] %s"), udp, zn_strerror(ret)); 501 | zn_deludp(udp->udp); 502 | udp->udp = NULL; 503 | } 504 | } break; 505 | case LK_CMD_UDP_RECVFROM: { 506 | lk_Udp *udp = cmd->u.udp; 507 | int ret = udp->udp ? zn_recvfrom(udp->udp, 508 | zn_buffer(&udp->buff), (unsigned)zn_bufflen(&udp->buff), 509 | lkX_onrecvfrom, udp) : ZN_OK; 510 | if (ret != ZN_OK) { 511 | lk_log(zs->S, "W[recvfrom]" lk_loc("[%p] %s"), udp, zn_strerror(ret)); 512 | zn_deludp(udp->udp); 513 | udp->udp = NULL; 514 | } 515 | } break; 516 | } 517 | lkX_putpooled(cmd); 518 | } 519 | 520 | static void lkX_post (lk_PostCmd *cmd) { 521 | if (zn_post(cmd->zs->zs, lkX_poster, cmd) != ZN_OK) 522 | lk_log(cmd->zs->S, "E[socket]" lk_loc("zn_post() error")); 523 | } 524 | 525 | static int lkX_refactor (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 526 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)lk_service(sender)); 527 | (void)sender; 528 | switch (sig->type) { 529 | default: return LK_ERR; 530 | 531 | case LK_SIGTYPE_ACCEPT_LISTEN: { 532 | lk_Accept *accept = (lk_Accept*)sig->data; 533 | if (accept->handler) 534 | accept->handler(S, accept->ud, accept->error, accept, accept->tcp); 535 | else if (accept->error != ZN_OK) { 536 | lk_Tcp *tcp = accept->tcp; 537 | if (tcp) lkX_putcached(tcp); 538 | lkX_putpooled(accept); 539 | } 540 | } break; 541 | 542 | case LK_SIGTYPE_TCP_CONNECT: { 543 | lk_PostCmd *cmd = (lk_PostCmd*)sig->data; 544 | if (cmd->h.on_connect) 545 | cmd->h.on_connect(S, cmd->data, cmd->error, cmd->u.tcp); 546 | else { 547 | lk_Tcp *tcp = cmd->u.tcp; 548 | lkX_putcached(tcp); 549 | } 550 | lkX_putpooled(cmd); 551 | } break; 552 | 553 | case LK_SIGTYPE_TCP_RECV: { 554 | lk_Tcp *tcp = (lk_Tcp*)sig->data; 555 | if (tcp->error != ZN_OK) { 556 | lk_RecvHandlers *h = tcp->handlers; 557 | if (h && h->on_header) 558 | h->on_header(S, h->ud_header, tcp, NULL, 0); 559 | lkX_putcached(tcp); 560 | } 561 | else if (zn_recvfinish(&tcp->recv, tcp->count)) { 562 | lkX_getpooled(cmd, lk_PostCmd); 563 | cmd->service = lk_self(zs->S); 564 | cmd->cmd = LK_CMD_TCP_RECV; 565 | cmd->u.tcp = tcp; 566 | lkX_post(cmd); 567 | } 568 | } break; 569 | 570 | case LK_SIGTYPE_UDP_BIND: { 571 | lk_PostCmd *cmd = (lk_PostCmd*)sig->data; 572 | if (cmd->h.on_udpbind) 573 | cmd->h.on_udpbind(S, cmd->data, 574 | cmd->u.udp ? ZN_ERROR : ZN_OK, cmd->u.udp); 575 | lkX_putpooled(cmd); 576 | } break; 577 | 578 | case LK_SIGTYPE_UDP_RECVFROM: { 579 | lk_Udp *udp = (lk_Udp*)sig->data; 580 | lk_RecvHandlers *h = udp->handlers; 581 | lkX_getpooled(cmd, lk_PostCmd); 582 | cmd->service = lk_self(zs->S); 583 | if (h && h->on_recvfrom) 584 | h->on_recvfrom(S, h->ud_recvfrom, udp, udp->error, 585 | zn_buffer(&udp->buff), udp->count, 586 | udp->info.addr, udp->info.port); 587 | cmd->cmd = LK_CMD_UDP_RECVFROM; 588 | cmd->u.udp = udp; 589 | lkX_post(cmd); } break; 590 | } 591 | return LK_OK; 592 | } 593 | 594 | 595 | /* interfaces */ 596 | 597 | static lk_RecvHandlers *lkX_sethandlers (lk_ZNetState *zs) { 598 | lk_HandlersEntry *e = 599 | (lk_HandlersEntry*)lk_settable(zs->S, &zs->handler_map, (const char*)lk_self(zs->S)); 600 | if (e->handlers == NULL) { 601 | e->handlers = (lk_RecvHandlers*)lk_poolalloc(zs->S, &zs->handlers); 602 | memset(e->handlers, 0, sizeof(lk_RecvHandlers)); 603 | } 604 | return e->handlers; 605 | } 606 | 607 | LK_API void lk_setonheader (lk_Service *svr, lk_HeaderHandler *h, void *ud) { 608 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)svr); 609 | lk_RecvHandlers *hs; 610 | lk_lock(zs->lock); 611 | hs = lkX_sethandlers(zs); 612 | hs->on_header = h; 613 | hs->ud_header = ud; 614 | lk_unlock(zs->lock); 615 | } 616 | 617 | LK_API void lk_setonpacket (lk_Service *svr, lk_PacketHandler *h, void *ud) { 618 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)svr); 619 | lk_RecvHandlers *hs; 620 | lk_lock(zs->lock); 621 | hs = lkX_sethandlers(zs); 622 | hs->on_packet = h; 623 | hs->ud_packet = ud; 624 | lk_unlock(zs->lock); 625 | } 626 | 627 | LK_API void lk_setonudpmsg (lk_Service *svr, lk_RecvFromHandler *h, void *ud) { 628 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)svr); 629 | lk_RecvHandlers *hs; 630 | lk_lock(zs->lock); 631 | hs = lkX_sethandlers(zs); 632 | hs->on_recvfrom = h; 633 | hs->ud_recvfrom = ud; 634 | lk_unlock(zs->lock); 635 | } 636 | 637 | LK_API lk_Accept *lk_newaccept (lk_Service *svr, lk_AcceptHandler *h, void *ud) { 638 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)svr); 639 | lkX_getpooled(accept, lk_Accept); 640 | accept->service = lk_self(zs->S); 641 | accept->handler = h; 642 | accept->ud = ud; 643 | return accept; 644 | } 645 | 646 | LK_API void lk_delaccept (lk_Accept *accept) { 647 | lk_ZNetState *zs = accept->zs; 648 | lkX_getpooled(cmd, lk_PostCmd); 649 | accept->service = lk_self(zs->S); 650 | cmd->cmd = LK_CMD_ACCEPT_DELETE; 651 | cmd->u.accept = accept; 652 | lkX_post(cmd); 653 | } 654 | 655 | LK_API void lk_listen (lk_Accept *accept, const char *addr, unsigned port) { 656 | lk_ZNetState *zs = accept->zs; 657 | lkX_getpooled(cmd, lk_PostCmd); 658 | accept->service = lk_self(zs->S); 659 | cmd->cmd = LK_CMD_ACCEPT_LISTEN; 660 | cmd->u.accept = accept; 661 | lkX_copyinfo(cmd, addr, port); 662 | lkX_post(cmd); 663 | } 664 | 665 | LK_API void lk_connect (lk_Service *svr, const char *addr, unsigned port, lk_ConnectHandler *h, void *ud) { 666 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)svr); 667 | lkX_getpooled(cmd, lk_PostCmd); 668 | cmd->service = lk_self(zs->S); 669 | cmd->cmd = LK_CMD_TCP_CONNECT; 670 | cmd->h.on_connect = h; 671 | cmd->data = ud; 672 | lkX_copyinfo(cmd, addr, port); 673 | lkX_post(cmd); 674 | } 675 | 676 | LK_API void *lk_gettcpdata (lk_Tcp *tcp) { 677 | lk_ZNetState *zs = tcp->zs; 678 | void *data; 679 | lk_lock(zs->lock); 680 | data = tcp->data; 681 | lk_unlock(zs->lock); 682 | return data; 683 | } 684 | 685 | LK_API void lk_settcpdata (lk_Tcp *tcp, void *data) { 686 | lk_ZNetState *zs = tcp->zs; 687 | lk_lock(zs->lock); 688 | tcp->data = data; 689 | lk_unlock(zs->lock); 690 | } 691 | 692 | LK_API void lk_deltcp (lk_Tcp *tcp) { 693 | lk_ZNetState *zs = tcp->zs; 694 | lkX_getpooled(cmd, lk_PostCmd); 695 | cmd->service = lk_self(zs->S); 696 | cmd->cmd = LK_CMD_TCP_DELETE; 697 | cmd->u.tcp = tcp; 698 | lkX_post(cmd); 699 | } 700 | 701 | LK_API void lk_send (lk_Tcp *tcp, const char *buff, unsigned size) { 702 | lk_ZNetState *zs = tcp->zs; 703 | lkX_getpooled(cmd, lk_PostCmd); 704 | cmd->service = lk_self(zs->S); 705 | cmd->cmd = LK_CMD_TCP_SEND; 706 | cmd->u.tcp = tcp; 707 | cmd->data = lk_newlstring(zs->S, buff, size); 708 | lkX_post(cmd); 709 | } 710 | 711 | LK_API void lk_bindudp (lk_Service *svr, const char *addr, unsigned port, lk_UdpBindHandler *h, void *ud) { 712 | lk_ZNetState *zs = (lk_ZNetState*)lk_data((lk_Slot*)svr); 713 | lkX_getpooled(cmd, lk_PostCmd); 714 | cmd->service = lk_self(zs->S); 715 | cmd->cmd = LK_CMD_UDP_BIND; 716 | cmd->h.on_udpbind = h; 717 | cmd->data = ud; 718 | lkX_copyinfo(cmd, addr, port); 719 | lkX_post(cmd); 720 | } 721 | 722 | LK_API void lk_deludp (lk_Udp *udp) { 723 | lk_ZNetState *zs = udp->zs; 724 | lkX_getpooled(cmd, lk_PostCmd); 725 | cmd->service = lk_self(zs->S); 726 | cmd->cmd = LK_CMD_UDP_DELETE; 727 | cmd->u.udp = udp; 728 | lkX_post(cmd); 729 | } 730 | 731 | LK_API void lk_sendto (lk_Udp *udp, const char *buff, unsigned size, const char *addr, unsigned port) { 732 | lk_ZNetState *zs = udp->zs; 733 | lkX_getpooled(cmd, lk_PostCmd); 734 | cmd->service = lk_self(zs->S); 735 | cmd->cmd = LK_CMD_UDP_SENDTO; 736 | cmd->u.udp = udp; 737 | cmd->data = lk_newlstring(zs->S, buff, size); 738 | lkX_copyinfo(cmd, addr, port); 739 | lkX_post(cmd); 740 | } 741 | 742 | 743 | /* entry point */ 744 | 745 | LKMOD_API int loki_service_socket (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 746 | lk_ZNetState *zs = lkX_state(S); 747 | if (sender == NULL) { 748 | zs = lkX_newstate(S); 749 | lk_Service *svr = lk_self(S); 750 | zs->poll = lk_newpoll(S, "poll", lkX_poller, zs); 751 | lk_setrefactor((lk_Slot*)svr, lkX_refactor); 752 | lk_setdata((lk_Slot*)svr, zs); 753 | return LK_WEAK; 754 | } 755 | else if (sig == NULL) 756 | zn_post(zs->zs, lkX_postdeletor, zs); 757 | return LK_OK; 758 | } 759 | 760 | /* win32cc: flags+='-s -mdll -xc' output='loki.dll' libs+='-lws2_32' 761 | * unixcc: flags+='-fPIC -shared -xc' output='loki.so' 762 | * cc: flags+='-Wextra -O3' input='service_*.c lokilib.c' */ 763 | 764 | -------------------------------------------------------------------------------- /loki.h: -------------------------------------------------------------------------------- 1 | #ifndef loki_h 2 | #define loki_h 3 | 4 | 5 | #ifndef LK_NS_BEGIN 6 | # ifdef __cplusplus 7 | # define LK_NS_BEGIN extern "C" { 8 | # define LK_NS_END } 9 | # else 10 | # define LK_NS_BEGIN 11 | # define LK_NS_END 12 | # endif 13 | #endif /* LK_NS_BEGIN */ 14 | 15 | #ifdef LK_STATIC_API 16 | # ifndef LOKI_IMPLEMENTATION 17 | # define LOKI_IMPLEMENTATION 18 | # endif 19 | # if __GNUC__ 20 | # define LK_API static __attribute((unused)) 21 | # else 22 | # define LK_API static 23 | # endif 24 | #endif 25 | 26 | #if !defined(LK_API) && defined(_WIN32) 27 | # if defined(LOKI_IMPLEMENTATION) || defined(LOKI_MODULE) 28 | # define LK_API __declspec(dllexport) 29 | # else 30 | # define LK_API __declspec(dllimport) 31 | # endif 32 | #endif 33 | 34 | #ifndef LK_API 35 | # define LK_API extern 36 | #endif 37 | 38 | #ifndef LKMOD_API 39 | # define LKMOD_API LK_API 40 | #endif 41 | 42 | #include 43 | #include 44 | 45 | #define LK_OK (0) 46 | #define LK_WEAK (1) 47 | #define LK_ERR (-1) 48 | #define LK_TIMEOUT (-2) 49 | 50 | #define LK_TYPE_MASK ((unsigned)0x3FFFFFFF) 51 | #define LK_RESPONSE_TYPE ((unsigned)0x80000000) 52 | 53 | #ifndef LK_SLOTNAME_LAUNCH 54 | # define LK_SLOTNAME_LAUNCH "on_service_launch" 55 | #endif 56 | 57 | #ifndef LK_SLOTNAME_CLOSE 58 | # define LK_SLOTNAME_CLOSE "on_service_close" 59 | #endif 60 | 61 | LK_NS_BEGIN 62 | 63 | 64 | typedef struct lk_State lk_State; 65 | typedef struct lk_Service lk_Service; 66 | typedef struct lk_Slot lk_Slot; 67 | typedef struct lk_Signal lk_Signal; 68 | typedef struct lk_Source lk_Source; 69 | 70 | typedef int lk_Handler (lk_State *S, lk_Slot *sender, lk_Signal *sig); 71 | typedef void *lk_Allocf (void *ud, void *ptr, size_t size, size_t osize); 72 | 73 | struct lk_Source { 74 | lk_Service *service; 75 | lk_Handler *callback; 76 | lk_Handler *deletor; 77 | void *ud; 78 | unsigned refcount : 31; 79 | unsigned force : 1; /* call source even in req signal */ 80 | }; 81 | 82 | struct lk_Signal { 83 | lk_Source *source; 84 | void *data; 85 | unsigned type : 30; 86 | unsigned isdata : 1; /* data is lk_Data* */ 87 | unsigned isack : 1; /* this is a response signal */ 88 | }; 89 | 90 | 91 | /* global routines */ 92 | 93 | LK_API lk_State *lk_newstate (const char *name, lk_Allocf *allocf, void *ud); 94 | LK_API void lk_close (lk_State *S); 95 | 96 | LK_API void lk_waitclose (lk_State *S); 97 | 98 | LK_API int lk_start (lk_State *S, int threads); 99 | 100 | LK_API char *lk_getconfig (lk_State *S, const char *key); 101 | LK_API void lk_setconfig (lk_State *S, const char *key, const char *value); 102 | 103 | LK_API int lk_log (lk_State *S, const char *fmt, ...); 104 | LK_API int lk_vlog (lk_State *S, const char *fmt, va_list l); 105 | 106 | #define lk_str_(str) # str 107 | #define lk_str(str) lk_str_(str) 108 | #define lk_loc(str) __FILE__ ":" lk_str(__LINE__) ": " str 109 | 110 | 111 | /* service routines */ 112 | 113 | LK_API lk_Service *lk_launch (lk_State *S, const char *name, lk_Handler *h, void *ud); 114 | 115 | LK_API int lk_retain (lk_Service *svr); 116 | LK_API int lk_release (lk_Service *svr); 117 | 118 | LK_API int lk_broadcast (lk_State *S, const char *slot, const lk_Signal *sig); 119 | 120 | LK_API lk_Service *lk_self (lk_State *S); 121 | 122 | 123 | /* message routines */ 124 | 125 | #define LK_SIGNAL { NULL, NULL, 0, 0, 0 } 126 | #define LK_RESPONSE { NULL, NULL, 0, 0, 1 } 127 | #define lk_serviceslot(slot) ((lk_Slot*)lk_service((lk_Slot*)(slot))) 128 | 129 | LK_API lk_Slot *lk_newslot (lk_State *S, const char *name, lk_Handler *h, void *ud); 130 | LK_API lk_Slot *lk_newpoll (lk_State *S, const char *name, lk_Handler *h, void *ud); 131 | LK_API lk_Slot *lk_slot (lk_State *S, const char *name); 132 | LK_API lk_Slot *lk_current (lk_State *S); 133 | 134 | LK_API int lk_wait (lk_State *S, lk_Signal *sig, int waitms); 135 | 136 | LK_API void lk_initsource (lk_State *S, lk_Source *src, lk_Handler *h, void *ud); 137 | LK_API void lk_usesource (lk_Source *src); 138 | LK_API void lk_freesource (lk_Source *src); 139 | LK_API void lk_setcallback (lk_State *S, lk_Handler *h, void *ud); 140 | 141 | LK_API int lk_emit (lk_Slot *slot, const lk_Signal *sig); 142 | LK_API int lk_emitstring (lk_Slot *slot, unsigned type, const char *s); 143 | 144 | LK_API void lk_sethook (lk_Slot *slot, lk_Handler *h, void *ud); 145 | LK_API void lk_setdata (lk_Slot *slot, void *data); 146 | 147 | LK_API void *lk_data (lk_Slot *slot); 148 | 149 | LK_API const char *lk_name (lk_Slot *slot); 150 | LK_API lk_Service *lk_service (lk_Slot *slot); 151 | LK_API lk_State *lk_state (lk_Slot *slot); 152 | 153 | LK_API lk_Handler *lk_slothandler (lk_Slot *slot); 154 | LK_API void lk_setslothandler (lk_Slot *slot, lk_Handler *h); 155 | 156 | LK_API lk_Handler *lk_refactor (lk_Slot *slot); 157 | LK_API void lk_setrefactor (lk_Slot *slot, lk_Handler *h); 158 | 159 | 160 | LK_NS_END 161 | 162 | #endif /* loki_h */ 163 | 164 | /****************************************************************************/ 165 | 166 | #ifndef lk_utils_h 167 | #define lk_utils_h 168 | 169 | #include 170 | 171 | LK_NS_BEGIN 172 | 173 | 174 | #define LK_MPOOLPAGESIZE 4096 175 | 176 | /* memory management */ 177 | 178 | #ifndef LK_DEBUG_POOL 179 | # ifdef _NDEBUG 180 | # define LK_DEBUG_POOL(x) /* nothing */ 181 | # else 182 | # define LK_DEBUG_POOL(x) x 183 | # endif 184 | #endif 185 | 186 | typedef struct lk_MemPool { 187 | void *pages; 188 | void *freed; 189 | size_t size; 190 | LK_DEBUG_POOL(size_t allocated;) 191 | } lk_MemPool; 192 | 193 | LK_API void *lk_malloc (lk_State *S, size_t size); 194 | LK_API void *lk_realloc (lk_State *S, void *ptr, size_t size, size_t osize); 195 | LK_API void lk_free (lk_State *S, void *ptr, size_t osize); 196 | LK_API void lk_initpool (lk_MemPool *mpool, size_t size); 197 | LK_API void lk_freepool (lk_State *S, lk_MemPool *mpool); 198 | LK_API void *lk_poolalloc (lk_State *S, lk_MemPool *mpool); 199 | LK_API void lk_poolfree (lk_MemPool *mpool, void *obj); 200 | 201 | 202 | /* string routines */ 203 | 204 | typedef struct lk_Data lk_Data; 205 | 206 | LK_API lk_Data *lk_newdata (lk_State *S, size_t size); 207 | LK_API size_t lk_deldata (lk_State *S, lk_Data *data); 208 | LK_API size_t lk_usedata (lk_State *S, lk_Data *data); 209 | 210 | LK_API size_t lk_len (lk_Data *data); 211 | LK_API size_t lk_size (lk_Data *data); 212 | LK_API void lk_setlen (lk_Data *data, size_t len); 213 | 214 | LK_API lk_Data *lk_newstring (lk_State *S, const char *s); 215 | LK_API lk_Data *lk_newlstring (lk_State *S, const char *s, size_t len); 216 | LK_API lk_Data *lk_newvfstring (lk_State *S, const char *fmt, va_list l); 217 | LK_API lk_Data *lk_newfstring (lk_State *S, const char *fmt, ...); 218 | 219 | LK_API int lk_emitdata (lk_Slot *slot, unsigned type, lk_Data *data); 220 | 221 | LK_API char *lk_strcpy (char *buff, const char *s, size_t len); 222 | LK_API int lk_vsnprintf (char *buff, size_t size, const char *fmt, va_list l); 223 | 224 | 225 | /* table routines */ 226 | 227 | typedef struct lk_Entry { 228 | int next; 229 | unsigned hash; 230 | const char *key; 231 | } lk_Entry; 232 | 233 | typedef struct lk_Table { 234 | size_t size; 235 | size_t entry_size; 236 | size_t lastfree; 237 | lk_Entry *hash; 238 | } lk_Table; 239 | 240 | typedef struct lk_PtrEntry { lk_Entry entry; void *data; } lk_PtrEntry; 241 | 242 | #define lk_key(e) (((lk_Entry*)(e))->key) 243 | 244 | LK_API void lk_inittable (lk_Table *t, size_t entry_size); 245 | LK_API void lk_copytable (lk_State *S, lk_Table *t, const lk_Table *other); 246 | LK_API void lk_freetable (lk_State *S, lk_Table *t); 247 | 248 | LK_API size_t lk_resizetable (lk_State *S, lk_Table *t, size_t len); 249 | 250 | LK_API lk_Entry *lk_newkey (lk_State *S, lk_Table *t, lk_Entry *entry); 251 | LK_API lk_Entry *lk_gettable (lk_Table *t, const char *key); 252 | LK_API lk_Entry *lk_settable (lk_State *S, lk_Table *t, const char *key); 253 | 254 | LK_API int lk_nextentry (lk_Table *t, lk_Entry **pentry); 255 | 256 | 257 | LK_NS_END 258 | 259 | #endif /* lk_utils_h */ 260 | 261 | 262 | #ifndef lk_thread_h 263 | #define lk_thread_h 264 | 265 | #ifdef _WIN32 266 | 267 | #ifndef WIN32_LEAN_AND_MEAN 268 | # define WIN32_LEAN_AND_MEAN 269 | #endif 270 | # include 271 | # include 272 | 273 | typedef DWORD lk_TlsKey; 274 | typedef CRITICAL_SECTION lk_Lock; 275 | typedef HANDLE lk_Event; 276 | typedef HANDLE lk_Thread; 277 | 278 | #define lk_inittls(key) ((*(key) = TlsAlloc()) != TLS_OUT_OF_INDEXES) 279 | #define lk_freetls(key) TlsFree(key) 280 | #define lk_gettls(key) TlsGetValue(key) 281 | #define lk_settls(key, p) TlsSetValue((key),(p)) 282 | 283 | #define lk_initlock(lock) (InitializeCriticalSection(lock), 1) 284 | #define lk_freelock(lock) DeleteCriticalSection(&(lock)) 285 | #define lk_lock(lock) EnterCriticalSection(&(lock)) 286 | #define lk_unlock(lock) LeaveCriticalSection(&(lock)) 287 | 288 | #define lk_initevent(evt) ((*(evt)=CreateEvent(NULL,FALSE,FALSE,NULL))!=NULL) 289 | #define lk_freeevent(evt) CloseHandle(evt) 290 | #define lk_signal(evt) SetEvent(evt) 291 | 292 | #define lk_waitthread(t) (WaitForSingleObject((t), INFINITE),(void)lk_freethread(t)) 293 | #define lk_freethread(t) ((void)CloseHandle(t)) 294 | 295 | #else /* POSIX systems */ 296 | 297 | #include 298 | #include 299 | #include 300 | #include 301 | 302 | typedef pthread_key_t lk_TlsKey; 303 | typedef pthread_mutex_t lk_Lock; 304 | typedef pthread_cond_t lk_Event; 305 | typedef pthread_t lk_Thread; 306 | 307 | #define lk_inittls(key) (pthread_key_create((key), NULL) == 0) 308 | #define lk_freetls(key) pthread_key_delete(key) 309 | #define lk_gettls(key) pthread_getspecific(key) 310 | #define lk_settls(key, p) pthread_setspecific((key), (p)) 311 | 312 | #define lk_initlock(lock) (pthread_mutex_init(lock, NULL) == 0) 313 | #define lk_freelock(lock) pthread_mutex_destroy(&(lock)) 314 | #define lk_lock(lock) pthread_mutex_lock(&(lock)) 315 | #define lk_unlock(lock) pthread_mutex_unlock(&(lock)) 316 | 317 | #define lk_initevent(evt) (pthread_cond_init((evt), NULL) == 0) 318 | #define lk_freeevent(evt) pthread_cond_destroy(&(evt)) 319 | #define lk_signal(evt) pthread_cond_signal(&(evt)) 320 | 321 | #define lk_waitthread(t) pthread_join((t),NULL) 322 | #define lk_freethread(t) pthread_cancel(t) 323 | 324 | #endif 325 | 326 | LK_NS_BEGIN 327 | 328 | 329 | typedef void lk_ThreadHandler (void *args); 330 | 331 | LK_API int lk_initthread (lk_Thread *t, lk_ThreadHandler *h, void *ud); 332 | LK_API int lk_cpucount (void); 333 | 334 | LK_API int lk_waitevent (lk_Event *evt, lk_Lock *lock, int waitms); 335 | 336 | 337 | LK_NS_END 338 | 339 | #endif /* lk_thread_h */ 340 | 341 | 342 | #ifndef lk_queue_h 343 | #define lk_queue_h 344 | 345 | #define lkQ_entry(T) T *next 346 | #define lkQ_type(T) struct { T *first; T **plast; } 347 | 348 | #define lkQ_init(h) ((h)->plast = &(h)->first, (h)->first = NULL) 349 | #define lkQ_clear(h, n) ((n) = (h)->first, lkQ_init(h)) 350 | #define lkQ_empty(h) ((h)->first == NULL) 351 | 352 | #define lkQ_merge(h, nh) ((void)((nh)->first && \ 353 | ((void)(!(h)->first && ((h)->first = (nh)->first)), \ 354 | ((h)->plast = (nh)->plast)))) 355 | 356 | #define lkQ_enqueue(h, n) (*(h)->plast = (n), \ 357 | (h)->plast = &(n)->next, (n)->next = NULL) 358 | #define lkQ_dequeue(h, n) ((n) = (h)->first, (void)((n) && (n)->next ? \ 359 | ((h)->first = (n)->next) : lkQ_init(h))) 360 | 361 | #endif /* lk_queue_h */ 362 | 363 | 364 | #ifndef lk_context_h 365 | #define lk_context_h 366 | 367 | 368 | #include 369 | 370 | #ifndef LK_ENABLE_PCALL 371 | # define lk_throw(S,c) (void)(c) 372 | # define lk_try(S,c,a) do { a; } while (0) 373 | # define lk_JmpBuf int /* dummy variable */ 374 | 375 | #elif defined(__cplusplus) && !defined(LK_USE_LONGJMP) 376 | # define lk_throw(S,c) throw(c) 377 | # define lk_try(S,c,a) do { try { a; } catch(lk_Context *c) {} } while (0) 378 | # define lk_JmpBuf int /* dummy variable */ 379 | 380 | #elif _WIN32 /* ISO C handling with long jumps */ 381 | # define lk_throw(S,c) longjmp((c)->b, 1) 382 | # define lk_try(S,c,a) do { if (setjmp((c)->b) == 0) { a; } } while (0) 383 | # define lk_JmpBuf jmp_buf 384 | 385 | #else /* in POSIX, try _longjmp/_setjmp (more efficient) */ 386 | # define lk_throw(L,c) _longjmp((c)->b, 1) 387 | # define lk_try(L,c,a) do { if (_setjmp((c)->b) == 0) { a; } } while (0) 388 | # define lk_JmpBuf jmp_buf 389 | #endif 390 | 391 | LK_NS_BEGIN 392 | 393 | 394 | typedef int lk_DeferHandler (lk_State *S, void *ud); 395 | 396 | typedef struct lk_Defer { 397 | lkQ_entry(struct lk_Defer); 398 | lk_DeferHandler *h; 399 | void *ud; 400 | } lk_Defer; 401 | 402 | typedef struct lk_Context { 403 | struct lk_Context *prev; 404 | lk_State *S; 405 | lk_Slot *current; 406 | lk_Defer *defers; 407 | void *userdata; 408 | lk_JmpBuf b; 409 | int retcode; /* error code */ 410 | } lk_Context; 411 | 412 | LK_API lk_Context *lk_context (lk_State *S); 413 | LK_API void *lk_userdata (lk_State *S); 414 | 415 | LK_API void lk_pushcontext (lk_State *S, lk_Context *ctx, lk_Slot *slot); 416 | LK_API void lk_popcontext (lk_State *S, lk_Context *ctx); 417 | 418 | LK_API int lk_discard (lk_State *S); 419 | LK_API int lk_defer (lk_State *S, lk_DeferHandler *h, void *ud); 420 | 421 | 422 | LK_NS_END 423 | 424 | #endif /* lk_context_h */ 425 | 426 | /****************************************************************************/ 427 | 428 | #if defined(LOKI_IMPLEMENTATION) && !defined(lk_implemented) 429 | #define lk_implemented 430 | 431 | 432 | #include 433 | #include 434 | #include 435 | #include 436 | 437 | 438 | #ifndef LK_NAME 439 | # define LK_NAME "root" 440 | #endif /* LK_NAME */ 441 | 442 | #define LK_MAX_THREADS 32 443 | #define LK_MAX_NAMESIZE 32 444 | #define LK_MAX_SLOTNAME 63 445 | #define LK_HASHLIMIT 5 446 | #define LK_MIN_HASHSIZE 8 447 | #define LK_MAX_SIZET (~(size_t)0u - 100) 448 | #define LK_MAX_DATASIZE ((size_t)(1<<24)-100) 449 | #define LK_SMALLPIECE_LEN (sizeof(lk_Entry)*LK_MIN_HASHSIZE) 450 | 451 | LK_NS_BEGIN 452 | 453 | 454 | /* structures */ 455 | 456 | typedef struct lk_Poll lk_Poll; 457 | 458 | typedef struct lk_SignalNode { 459 | lkQ_entry(struct lk_SignalNode); 460 | lk_Slot *sender; 461 | lk_Slot *recipient; 462 | lk_Signal data; 463 | } lk_SignalNode; 464 | 465 | struct lk_Slot { 466 | char name[LK_MAX_SLOTNAME]; 467 | unsigned char flags; 468 | lk_State *S; 469 | lk_Service *service; 470 | void *userdata; 471 | lk_Handler *handler; 472 | lk_Handler *refactor; 473 | lk_Handler *hookf; 474 | void *hook_ud; 475 | lk_Source *source; 476 | lk_SignalNode *current; 477 | lkQ_entry(lk_Slot); /* all slots in same service */ 478 | }; 479 | 480 | struct lk_Poll { 481 | lk_Slot slot; 482 | lk_Thread thread; 483 | lk_Event event; 484 | lk_Lock lock; 485 | lkQ_type(lk_SignalNode) signals; 486 | }; 487 | 488 | struct lk_Service { 489 | lk_Slot slot; 490 | lk_Slot *slots; 491 | lk_Lock lock; 492 | unsigned pending; 493 | lkQ_entry(lk_Service); 494 | lkQ_type(lk_SignalNode) signals; 495 | }; 496 | 497 | struct lk_State { 498 | lk_Service root; 499 | int nservices; 500 | int nthreads; 501 | lk_Table slot_names; 502 | lk_Slot *logger; 503 | lk_Lock lock; 504 | 505 | lkQ_type(lk_Service) main_queue; 506 | lk_Event queue_event; 507 | lk_Lock queue_lock; 508 | 509 | lk_MemPool services; 510 | lk_MemPool slots; 511 | lk_MemPool polls; 512 | lk_MemPool defers; 513 | lk_MemPool signals; 514 | lk_MemPool sources; 515 | lk_MemPool smallpieces; 516 | lk_Lock pool_lock; 517 | 518 | lk_Table config; 519 | lk_Lock config_lock; 520 | 521 | lk_Allocf *allocf; 522 | void *alloc_ud; 523 | lk_TlsKey tls_index; 524 | lk_Thread threads[LK_MAX_THREADS]; 525 | }; 526 | 527 | 528 | /* memory management */ 529 | 530 | #ifndef va_copy 531 | # ifdef __va_copy 532 | # define va_copy __va_copy 533 | # else 534 | # define va_copy(a,b) (*(a)=*(b)) 535 | # endif 536 | #endif 537 | 538 | struct lk_Data { 539 | unsigned size : 24; 540 | unsigned len : 24; 541 | unsigned refcount : 16; 542 | }; 543 | 544 | LK_API size_t lk_len (lk_Data *data) { return data-- ? data->len : 0; } 545 | LK_API size_t lk_size (lk_Data *data) { return data-- ? data->size : 0; } 546 | 547 | LK_API void lk_setlen(lk_Data *data, size_t len) 548 | { if (data--) data->len = len < data->size ? (unsigned)len : data->size; } 549 | 550 | static void *lkM_outofmemory (void) 551 | { fprintf(stderr, "out of memory\n"); abort(); return NULL; } 552 | 553 | LK_API lk_Data *lk_newstring (lk_State *S, const char *s) 554 | { return lk_newlstring(S, s, strlen(s)); } 555 | 556 | LK_API int lk_vsnprintf (char *buff, size_t size, const char *fmt, va_list l) { 557 | #if !defined(_WIN32) || defined(__MINGW32__) 558 | return vsnprintf(buff, size, fmt, l); 559 | #else 560 | int count = -1; 561 | if (size != 0) { 562 | va_list nl; 563 | va_copy(nl, l); 564 | count = _vsnprintf_s(buff, size, _TRUNCATE, fmt, nl); 565 | va_end(nl); 566 | } 567 | if (count == -1) count = _vscprintf(fmt, l); 568 | return count; 569 | #endif 570 | } 571 | 572 | LK_API char *lk_strcpy (char *buff, const char *s, size_t len) { 573 | size_t srclen = strlen(s); 574 | if (srclen >= len - 1) { 575 | memcpy(buff, s, len-1); 576 | buff[len-1] = '\0'; 577 | } 578 | else { 579 | memcpy(buff, s, srclen); 580 | memset(buff+srclen, 0, len-srclen); 581 | } 582 | return buff; 583 | } 584 | 585 | LK_API void *lk_malloc (lk_State *S, size_t size) { 586 | void *newptr; 587 | if (size > LK_SMALLPIECE_LEN) 588 | newptr = S->allocf(S->alloc_ud, NULL, size, 0); 589 | else { 590 | lk_lock(S->pool_lock); 591 | newptr = lk_poolalloc(S, &S->smallpieces); 592 | lk_unlock(S->pool_lock); 593 | } 594 | if (newptr == NULL) return lkM_outofmemory(); 595 | return newptr; 596 | } 597 | 598 | LK_API void *lk_realloc (lk_State *S, void *ptr, size_t size, size_t osize) { 599 | void *newptr; 600 | if (osize <= LK_SMALLPIECE_LEN && size <= LK_SMALLPIECE_LEN) 601 | return ptr; 602 | else if (osize > LK_SMALLPIECE_LEN) { 603 | newptr = S->allocf(S->alloc_ud, ptr, size, osize); 604 | if (newptr == NULL) return lkM_outofmemory(); 605 | } 606 | else { 607 | newptr = lk_malloc(S, size); 608 | memcpy(newptr, ptr, osize); 609 | lk_free(S, ptr, osize); 610 | } 611 | return newptr; 612 | } 613 | 614 | LK_API void lk_free (lk_State *S, void *ptr, size_t osize) { 615 | void *newptr = NULL; 616 | if (ptr == NULL) return; 617 | if (osize > LK_SMALLPIECE_LEN) 618 | newptr = S->allocf(S->alloc_ud, ptr, 0, osize); 619 | else { 620 | lk_lock(S->pool_lock); 621 | lk_poolfree(&S->smallpieces, ptr); 622 | lk_unlock(S->pool_lock); 623 | } 624 | assert(newptr == NULL); 625 | } 626 | 627 | LK_API void lk_initpool (lk_MemPool *mpool, size_t size) { 628 | const size_t sp = sizeof(void*); 629 | assert(((sp - 1) & sp) == 0); 630 | mpool->pages = NULL; 631 | mpool->freed = NULL; 632 | if (size < sp) size = sp; 633 | if (size % sp != 0) size = (size + sp - 1) & ~(sp - 1); 634 | mpool->size = size; 635 | LK_DEBUG_POOL(mpool->allocated = 0); 636 | assert(LK_MPOOLPAGESIZE / size > 2); 637 | } 638 | 639 | LK_API void lk_freepool (lk_State *S, lk_MemPool *mpool) { 640 | const size_t offset = LK_MPOOLPAGESIZE - sizeof(void*); 641 | LK_DEBUG_POOL(assert(mpool->allocated == 0)); 642 | while (mpool->pages != NULL) { 643 | void *next = *(void**)((char*)mpool->pages + offset); 644 | lk_free(S, mpool->pages, LK_MPOOLPAGESIZE); 645 | mpool->pages = next; 646 | } 647 | lk_initpool(mpool, mpool->size); 648 | } 649 | 650 | LK_API void *lk_poolalloc (lk_State *S, lk_MemPool *mpool) { 651 | void *obj = mpool->freed; 652 | LK_DEBUG_POOL(++mpool->allocated); 653 | if (obj == NULL) { 654 | const size_t offset = LK_MPOOLPAGESIZE - sizeof(void*); 655 | void *end, *newpage = lk_malloc(S, LK_MPOOLPAGESIZE); 656 | *(void**)((char*)newpage + offset) = mpool->pages; 657 | mpool->pages = newpage; 658 | end = (char*)newpage + (offset/mpool->size-1)*mpool->size; 659 | while (end != newpage) { 660 | *(void**)end = mpool->freed; 661 | mpool->freed = end; 662 | end = (char*)end - mpool->size; 663 | } 664 | return end; 665 | } 666 | mpool->freed = *(void**)obj; 667 | return obj; 668 | } 669 | 670 | LK_API void lk_poolfree (lk_MemPool *mpool, void *obj) { 671 | LK_DEBUG_POOL(--mpool->allocated); 672 | LK_DEBUG_POOL(assert((signed)mpool->allocated >= 0)); 673 | *(void**)obj = mpool->freed; 674 | mpool->freed = obj; 675 | } 676 | 677 | LK_API lk_Data *lk_newdata (lk_State *S, size_t size) { 678 | size_t rawlen = sizeof(lk_Data) + size; 679 | lk_Data *data = (lk_Data*)lk_malloc(S, rawlen); 680 | assert(size < LK_MAX_DATASIZE); 681 | data->size = (unsigned)size; 682 | data->len = 0; 683 | data->refcount = 0; 684 | if (rawlen <= LK_SMALLPIECE_LEN) 685 | data->size = LK_SMALLPIECE_LEN - sizeof(lk_Data); 686 | return data + 1; 687 | } 688 | 689 | LK_API size_t lk_usedata (lk_State *S, lk_Data *data) { 690 | size_t refcount; 691 | if (data-- == NULL) return 0; 692 | lk_lock(S->lock); 693 | refcount = ++data->refcount; 694 | lk_unlock(S->lock); 695 | return refcount; 696 | } 697 | 698 | LK_API size_t lk_deldata (lk_State *S, lk_Data *data) { 699 | size_t refcount = 0; 700 | if (data-- == NULL) return 0; 701 | lk_lock(S->lock); 702 | if (data->refcount > 1) refcount = --data->refcount; 703 | lk_unlock(S->lock); 704 | if (refcount == 0) lk_free(S, data, data->size + sizeof(lk_Data)); 705 | return refcount; 706 | } 707 | 708 | LK_API lk_Data *lk_newlstring (lk_State *S, const char *s, size_t len) { 709 | lk_Data *data = lk_newdata(S, len+1); 710 | memcpy(data, s, len); 711 | ((char*)data)[len] = '\0'; 712 | lk_setlen(data, len); 713 | return data; 714 | } 715 | 716 | LK_API lk_Data *lk_newvfstring (lk_State *S, const char *fmt, va_list l) { 717 | va_list nl; 718 | lk_Data *data; 719 | int len; 720 | va_copy(nl, l); 721 | len = lk_vsnprintf(NULL, 0, fmt, nl); 722 | va_end(nl); 723 | if (len <= 0) return NULL; 724 | data = lk_newdata(S, len+1); 725 | lk_vsnprintf((char*)data, len+1, fmt, l); 726 | lk_setlen(data, len); 727 | return data; 728 | } 729 | 730 | LK_API lk_Data *lk_newfstring (lk_State *S, const char *fmt, ...) { 731 | lk_Data *data; 732 | va_list l; 733 | va_start(l, fmt); 734 | data = lk_newvfstring(S, fmt, l); 735 | va_end(l); 736 | return data; 737 | } 738 | 739 | 740 | /* hashtable routines */ 741 | 742 | #define lk_offset(lhs, rhs) ((int)((char*)(lhs) - (char*)(rhs))) 743 | #define lk_index(lhs, rhs) ((lk_Entry*)((char*)(lhs) + (rhs))) 744 | 745 | LK_API void lk_inittable (lk_Table *t, size_t entry_size) 746 | { memset(t, 0, sizeof(*t)); t->entry_size = entry_size; } 747 | 748 | LK_API void lk_freetable (lk_State *S, lk_Table *t) 749 | { lk_free(S, t->hash, t->size*t->entry_size); lk_inittable(t, t->entry_size); } 750 | 751 | static size_t lkH_hashsize (lk_Table *t, size_t len) { 752 | size_t newsize = LK_MIN_HASHSIZE; 753 | const size_t maxsize = LK_MAX_SIZET/2/t->entry_size; 754 | while (newsize < maxsize && newsize < len) 755 | newsize <<= 1; 756 | assert(newsize < maxsize); 757 | return newsize < maxsize ? newsize : 0; 758 | } 759 | 760 | static size_t lkH_countsize (lk_Table *t) { 761 | size_t i, size = t->size * t->entry_size; 762 | size_t count = 0; 763 | for (i = 0; i < size; i += t->entry_size) { 764 | lk_Entry *e = lk_index(t->hash, i); 765 | if (e->key != NULL) ++count; 766 | } 767 | return count; 768 | } 769 | 770 | static lk_Entry *lkH_mainposition (lk_Table *t, unsigned hash) { 771 | assert((t->size & (t->size - 1)) == 0); 772 | return lk_index(t->hash, (hash & (t->size - 1))*t->entry_size); 773 | } 774 | 775 | static unsigned lkH_calchash (const char *s, size_t len) { 776 | size_t l1; 777 | size_t step = (len >> LK_HASHLIMIT) + 1; 778 | unsigned h = (unsigned)len; 779 | for (l1 = len; l1 >= step; l1 -= step) 780 | h ^= (h<<5) + (h>>2) + (unsigned char)s[l1 - 1]; 781 | return h; 782 | } 783 | 784 | static lk_Entry *lkH_get (lk_Table *t, const char *key, unsigned hash) { 785 | lk_Entry *e = lkH_mainposition(t, hash); 786 | for (;;) { 787 | if (e->key && (e->hash == hash && strcmp(e->key, key) == 0)) 788 | return e; 789 | if (e->next == 0) return NULL; 790 | e = lk_index(e, e->next); 791 | } 792 | } 793 | 794 | LK_API size_t lk_resizetable (lk_State *S, lk_Table *t, size_t len) { 795 | size_t i, size = t->size*t->entry_size; 796 | lk_Table nt = *t; 797 | nt.size = lkH_hashsize(t, len); 798 | if (nt.size == 0) return 0; 799 | nt.lastfree = nt.size*nt.entry_size; 800 | nt.hash = (lk_Entry*)lk_malloc(S, nt.lastfree); 801 | memset(nt.hash, 0, nt.lastfree); 802 | for (i = 0; i < size; i += t->entry_size) { 803 | lk_Entry *olde = lk_index(t->hash, i); 804 | lk_Entry *newe = lk_newkey(S, &nt, olde); 805 | assert(newe != NULL); 806 | if (newe != NULL && t->entry_size > sizeof(lk_Entry)) 807 | memcpy(newe + 1, olde + 1, t->entry_size - sizeof(lk_Entry)); 808 | } 809 | lk_free(S, t->hash, size); 810 | *t = nt; 811 | return t->size; 812 | } 813 | 814 | LK_API void lk_copytable (lk_State *S, lk_Table *nt, const lk_Table *t) { 815 | size_t size = t->size*t->entry_size; 816 | *nt = *t; 817 | nt->hash = (lk_Entry*)lk_malloc(S, size); 818 | memcpy(nt->hash, t->hash, size); 819 | } 820 | 821 | LK_API lk_Entry *lk_gettable (lk_Table *t, const char *key) { 822 | if (t->size == 0 || key == NULL) return NULL; 823 | return lkH_get(t, key, lkH_calchash(key, strlen(key))); 824 | } 825 | 826 | LK_API lk_Entry *lk_settable (lk_State *S, lk_Table *t, const char *key) { 827 | lk_Entry e, *ret; 828 | if (key == NULL) return NULL; 829 | e.key = key; 830 | e.hash = lkH_calchash(key, strlen(key)); 831 | if (t->size != 0 && (ret = lkH_get(t, key, e.hash)) != NULL) 832 | return ret; 833 | return lk_newkey(S, t, &e); 834 | } 835 | 836 | LK_API lk_Entry *lk_newkey (lk_State *S, lk_Table *t, lk_Entry *entry) { 837 | lk_Entry *mp; 838 | if (entry->key == NULL || 839 | (t->size == 0 && lk_resizetable(S, t, LK_MIN_HASHSIZE) == 0)) 840 | return NULL; 841 | redo: 842 | mp = lkH_mainposition(t, entry->hash); 843 | if (mp->key != NULL) { 844 | lk_Entry *f = NULL, *othern; 845 | while (t->lastfree > 0) { 846 | lk_Entry *e = lk_index(t->hash, t->lastfree -= t->entry_size); 847 | if (e->key == NULL && e->next == 0) { f = e; break; } 848 | } 849 | if (f == NULL) { 850 | if (lk_resizetable(S, t, lkH_countsize(t)*2) == 0) return NULL; 851 | goto redo; /* return lkH_newkey(t, entry); */ 852 | } 853 | othern = lkH_mainposition(t, mp->hash); 854 | if (othern != mp) { 855 | lk_Entry *next; 856 | while ((next = lk_index(othern, othern->next)) != mp) 857 | othern = next; 858 | othern->next = lk_offset(f, othern); 859 | memcpy(f, mp, t->entry_size); 860 | if (mp->next != 0) f->next += lk_offset(mp, f), mp->next = 0; 861 | } 862 | else { 863 | if (mp->next != 0) f->next = lk_offset(mp, f) + mp->next; 864 | else assert(f->next == 0); 865 | mp->next = lk_offset(f, mp), mp = f; 866 | } 867 | } 868 | mp->key = entry->key; 869 | mp->hash = entry->hash; 870 | if (t->entry_size > sizeof(lk_Entry)) 871 | memset(mp + 1, 0, t->entry_size - sizeof(lk_Entry)); 872 | return mp; 873 | } 874 | 875 | LK_API int lk_nextentry (lk_Table *t, lk_Entry **pentry) { 876 | const size_t size = t->size * t->entry_size; 877 | ptrdiff_t i = *pentry == NULL ? 0 : 878 | ((char*)*pentry - (char*)t->hash) + t->entry_size; 879 | assert(i >= 0 && (size_t)i <= size); 880 | for (; (size_t)i < size; i += t->entry_size) { 881 | lk_Entry *e = lk_index(t->hash, i); 882 | if (e->key != NULL) { *pentry = e; return 1; } 883 | } 884 | *pentry = NULL; 885 | return 0; 886 | } 887 | 888 | 889 | /* context routines */ 890 | 891 | LK_API lk_Context *lk_context (lk_State *S) 892 | { return S ? (lk_Context*)lk_gettls(S->tls_index) : NULL; } 893 | 894 | LK_API void *lk_userdata (lk_State *S) 895 | { lk_Context *ctx = lk_context(S); return ctx ? ctx->userdata : NULL; } 896 | 897 | static void lkC_calldefers (lk_State *S, lk_Context *ctx) { 898 | if (ctx->defers != NULL) { 899 | lk_Defer *defers = ctx->defers; 900 | while (defers != NULL) { 901 | lk_Defer *next = defers->next; 902 | defers->h(S, defers->ud); 903 | defers = next; 904 | } 905 | lk_lock(S->pool_lock); 906 | while (defers != NULL) { 907 | lk_Defer *next = defers->next; 908 | lk_poolfree(&S->defers, defers); 909 | defers = next; 910 | } 911 | lk_unlock(S->pool_lock); 912 | ctx->defers = NULL; 913 | } 914 | } 915 | 916 | LK_API void lk_pushcontext (lk_State *S, lk_Context *ctx, lk_Slot *slot) { 917 | ctx->prev = lk_context(S); 918 | ctx->S = S; 919 | ctx->current = slot; 920 | ctx->defers = NULL; 921 | ctx->userdata = slot ? slot->userdata : NULL; 922 | ctx->retcode = LK_OK; 923 | lk_settls(S->tls_index, ctx); 924 | } 925 | 926 | LK_API void lk_popcontext (lk_State *S, lk_Context *ctx) { 927 | if (ctx == NULL) return; 928 | lkC_calldefers(S, ctx); 929 | lk_settls(S->tls_index, ctx->prev); 930 | } 931 | 932 | LK_API int lk_discard (lk_State *S) { 933 | lk_Context *ctx = lk_context(S); 934 | if (ctx == NULL) { 935 | fprintf(stderr, "unproected errors\n"); 936 | abort(); 937 | } 938 | lkC_calldefers(S, ctx); 939 | ctx->retcode = LK_ERR; 940 | lk_throw(S, ctx); 941 | return LK_ERR; 942 | } 943 | 944 | LK_API int lk_defer (lk_State *S, lk_DeferHandler *h, void *ud) { 945 | lk_Context *ctx = lk_context(S); 946 | lk_Defer *defer; 947 | if (ctx == NULL) 948 | return LK_ERR; 949 | lk_lock(S->pool_lock); 950 | defer = (lk_Defer*)lk_poolalloc(S, &S->defers); 951 | lk_unlock(S->pool_lock); 952 | defer->h = h; 953 | defer->ud = ud; 954 | defer->next = ctx->defers; 955 | ctx->defers = defer; 956 | return LK_OK; 957 | } 958 | 959 | 960 | /* thread routines */ 961 | 962 | typedef struct lk_ThreadContext { 963 | lk_ThreadHandler *h; 964 | void *ud; 965 | } lk_ThreadContext; 966 | 967 | #ifdef _WIN32 968 | 969 | static unsigned __stdcall lkT_worker (void *lpParameter) { 970 | lk_ThreadContext ctx = *(lk_ThreadContext*)lpParameter; 971 | free(lpParameter); 972 | ctx.h(ctx.ud); 973 | return 0; 974 | } 975 | 976 | LK_API int lk_initthread (lk_Thread *t, lk_ThreadHandler *h, void *ud) { 977 | lk_ThreadContext *ctx = (lk_ThreadContext*)malloc(sizeof(lk_ThreadContext)); 978 | if (ctx == NULL) return 0; 979 | ctx->h = h; 980 | ctx->ud = ud; 981 | *t = (HANDLE)_beginthreadex(NULL, 0, lkT_worker, ctx, 0, NULL); 982 | return *t != NULL; 983 | } 984 | 985 | LK_API int lk_waitevent (lk_Event *evt, lk_Lock *lock, int waitms) { 986 | DWORD ret; 987 | lk_unlock(*lock); 988 | ret = WaitForSingleObject(*evt, waitms < 0 ? INFINITE : (DWORD)waitms); 989 | lk_lock(*lock); 990 | return ret != WAIT_FAILED ? LK_OK : LK_ERR; 991 | } 992 | 993 | #else 994 | 995 | #include 996 | 997 | static void *lkT_worker (void *ud) { 998 | lk_ThreadContext ctx = *(lk_ThreadContext*)ud; 999 | free(ud); 1000 | ctx.h(ctx.ud); 1001 | return NULL; 1002 | } 1003 | 1004 | LK_API int lk_initthread (lk_Thread *t, lk_ThreadHandler *h, void *ud) { 1005 | lk_ThreadContext *ctx = (lk_ThreadContext*)malloc(sizeof(lk_ThreadContext)); 1006 | if (ctx == NULL) return 0; 1007 | ctx->h = h; 1008 | ctx->ud = ud; 1009 | return pthread_create(t, NULL, lkT_worker, ctx) == 0; 1010 | } 1011 | 1012 | LK_API int lk_waitevent (lk_Event *evt, lk_Lock *lock, int waitms) { 1013 | int ret; 1014 | if (waitms < 0) 1015 | ret = pthread_cond_wait(evt, lock); 1016 | else { 1017 | struct timeval tv; 1018 | struct timespec ts; 1019 | int sec = waitms / 1000; 1020 | int usec = waitms % 1000 * 1000; 1021 | gettimeofday(&tv, NULL); 1022 | if (tv.tv_usec + usec > 1000000) { 1023 | sec += 1; 1024 | usec = (tv.tv_usec + usec) - 1000000; 1025 | } 1026 | ts.tv_sec = tv.tv_sec + sec; 1027 | ts.tv_nsec = (tv.tv_usec + usec) * 1000; 1028 | ret = pthread_cond_timedwait(evt, lock, &ts); 1029 | } 1030 | return ret == 0 || ret == ETIMEDOUT ? LK_OK : LK_ERR; 1031 | } 1032 | 1033 | #endif 1034 | 1035 | 1036 | /* slot/poll routines */ 1037 | 1038 | #define lkP_ispoll(obj) ((((lk_Slot*)(obj))->flags & 0x01) != 0) 1039 | #define lkP_issvr(obj) ((((lk_Slot*)(obj))->flags & 0x02) != 0) 1040 | #define lkP_isweak(obj) ((((lk_Slot*)(obj))->flags & 0x04) != 0) 1041 | #define lkP_isdead(obj) ((((lk_Slot*)(obj))->flags & 0x08) != 0) 1042 | #define lkP_isactive(obj) ((((lk_Slot*)(obj))->flags & 0x10) != 0) 1043 | 1044 | #define lkP_setpoll(obj) (((lk_Slot*)(obj))->flags |= 0x01) 1045 | #define lkP_setsvr(obj) (((lk_Slot*)(obj))->flags |= 0x02) 1046 | #define lkP_setweak(obj) (((lk_Slot*)(obj))->flags |= 0x04) 1047 | #define lkP_setdead(obj) (((lk_Slot*)(obj))->flags |= 0x08) 1048 | #define lkP_setactive(obj) (((lk_Slot*)(obj))->flags |= 0x10) 1049 | #define lkP_clractive(obj) (((lk_Slot*)(obj))->flags &= ~0x10) 1050 | 1051 | #define lkP_getter(name, type, field) \ 1052 | LK_API type lk_##name (lk_Slot *slot) { return slot ? slot->field : NULL; } 1053 | #define lkP_setter(name, type, field) \ 1054 | LK_API void lk_set##name (lk_Slot *s, type v) { if (s) s->field = v; } 1055 | lkP_getter(name, const char *, name ) 1056 | lkP_getter(service, lk_Service *, service ) 1057 | lkP_getter(state, lk_State *, S ) 1058 | lkP_getter(data, void *, userdata ) 1059 | lkP_setter(data, void *, userdata ) 1060 | lkP_getter(slothandler, lk_Handler *, handler ) 1061 | lkP_setter(slothandler, lk_Handler *, handler ) 1062 | lkP_getter(refactor, lk_Handler *, refactor ) 1063 | lkP_setter(refactor, lk_Handler *, refactor ) 1064 | #undef lkP_getter 1065 | #undef lkP_setter 1066 | 1067 | LK_API lk_Slot *lk_current (lk_State *S) 1068 | { lk_Context *ctx = lk_context(S); return ctx ? ctx->current : &S->root.slot; } 1069 | 1070 | static void lkP_name (char *buff, const char *svr, const char *name) { 1071 | size_t svrlen = strlen(svr); 1072 | assert(svrlen < LK_MAX_NAMESIZE); 1073 | memcpy(buff, svr, svrlen); buff += svrlen; 1074 | if (name != NULL) { 1075 | size_t namelen = strlen(name); 1076 | *buff++ = '.'; 1077 | assert(namelen < LK_MAX_NAMESIZE); 1078 | memcpy(buff, name, namelen); buff += namelen; 1079 | } 1080 | *buff = '\0'; 1081 | } 1082 | 1083 | static lk_Slot *lkP_new (lk_State *S, lk_MemPool *pool, lk_Service *svr, const char *name) { 1084 | lk_Slot *slot = NULL; 1085 | lk_lock(S->pool_lock); 1086 | slot = (lk_Slot*)lk_poolalloc(S, pool); 1087 | lk_unlock(S->pool_lock); 1088 | memset(slot, 0, pool->size); 1089 | lkP_name(slot->name, (const char*)svr, name); 1090 | slot->S = S; 1091 | slot->service = svr; 1092 | return slot; 1093 | } 1094 | 1095 | static lk_Slot *lkP_register (lk_State *S, lk_Slot *slot) { 1096 | lk_Entry *e = lk_settable(S, &S->slot_names, slot->name); 1097 | if (S->nthreads != 0 && (lk_Slot*)e->key == slot) 1098 | return slot; 1099 | if (&slot->service->slot == slot) 1100 | lk_freelock(slot->service->lock); 1101 | lk_lock(S->pool_lock); 1102 | if (lkP_issvr(slot)) lk_poolfree(&S->services, slot); 1103 | else if (lkP_ispoll(slot)) lk_poolfree(&S->polls, slot); 1104 | else lk_poolfree(&S->slots, slot); 1105 | lk_unlock(S->pool_lock); 1106 | return NULL; 1107 | } 1108 | 1109 | static int lkP_delpoll (lk_State *S, lk_Poll *poll) { 1110 | if (!lkP_isdead(poll)) { 1111 | lk_lock(poll->lock); 1112 | lkP_setdead(poll); 1113 | lk_signal(poll->event); 1114 | lk_unlock(poll->lock); 1115 | lk_waitthread(poll->thread); 1116 | } 1117 | if (poll->slot.service->pending != 0) 1118 | return LK_ERR; 1119 | lk_freeevent(poll->event); 1120 | lk_freelock(poll->lock); 1121 | lk_lock(S->pool_lock); 1122 | lk_poolfree(&S->polls, poll); 1123 | lk_unlock(S->pool_lock); 1124 | return LK_OK; 1125 | } 1126 | 1127 | static void lkP_poller (void *ud) { 1128 | lk_Context ctx; 1129 | lk_Signal sig = LK_SIGNAL; 1130 | lk_Poll *poll = (lk_Poll*)ud; 1131 | lk_State *S = poll->slot.S; 1132 | lk_pushcontext(S, &ctx, &poll->slot); 1133 | lk_try(S, &ctx, poll->slot.handler(S, &poll->slot, &sig)); 1134 | lkP_setdead(poll); 1135 | while (lk_wait(S, &sig, 0) == LK_OK) 1136 | ; 1137 | lk_popcontext(S, &ctx); 1138 | } 1139 | 1140 | static int lkP_startpoll (lk_Poll *poll) { 1141 | lk_State *S = poll->slot.S; 1142 | lkQ_init(&poll->signals); 1143 | if (lk_initlock(&poll->lock)) { 1144 | if (lk_initevent(&poll->event)) { 1145 | if (lk_initthread(&poll->thread, lkP_poller, poll)) 1146 | return LK_OK; 1147 | lk_freeevent(poll->event); 1148 | } 1149 | lk_freelock(poll->lock); 1150 | } 1151 | lk_lock(S->pool_lock); 1152 | lk_poolfree(&S->polls, poll); 1153 | lk_unlock(S->pool_lock); 1154 | return LK_ERR; 1155 | } 1156 | 1157 | static lk_Slot *lkP_findslotG (lk_State *S, const char *name) { 1158 | lk_Slot *slot = NULL; 1159 | lk_Entry *e; 1160 | lk_lock(S->lock); 1161 | e = lk_gettable(&S->slot_names, name); 1162 | if (e != NULL) slot = (lk_Slot*)e->key; 1163 | lk_unlock(S->lock); 1164 | return slot; 1165 | } 1166 | 1167 | static int lkP_check (lk_State *S, const char *tag, const char *name) { 1168 | if (name == NULL) 1169 | lk_log(S, "E[%s]" lk_loc("slot name required")); 1170 | else if (strlen(name) >= LK_MAX_NAMESIZE) 1171 | lk_log(S, "E[%s]" lk_loc("slot name '%s' too long"), tag, name); 1172 | else if (!lkP_isdead(lk_self(S))) 1173 | return LK_OK; 1174 | return LK_ERR; 1175 | } 1176 | 1177 | LK_API lk_Slot *lk_newslot (lk_State *S, const char *name, lk_Handler *h, void *ud) { 1178 | lk_Service *svr = lk_self(S); 1179 | lk_Slot *slot = NULL; 1180 | if (S == NULL || svr == NULL || lkP_check(S, "newslot", name) != LK_OK) 1181 | return NULL; 1182 | slot = lkP_new(S, &S->slots, svr, name); 1183 | slot->handler = h; 1184 | slot->userdata = ud; 1185 | lk_lock(S->lock); 1186 | if ((slot = lkP_register(S, slot)) != NULL) { 1187 | slot->next = svr->slots; 1188 | svr->slots = slot; 1189 | } 1190 | lk_unlock(S->lock); 1191 | if (slot == NULL) 1192 | lk_log(S, "E[newslot]", lk_loc("slot '%s' exists"), name); 1193 | return slot; 1194 | } 1195 | 1196 | LK_API lk_Slot *lk_newpoll (lk_State *S, const char *name, lk_Handler *h, void *ud) { 1197 | lk_Service *svr = lk_self(S); 1198 | lk_Poll *poll; 1199 | if (S == NULL || svr == NULL || lkP_check(S, "newpoll", name) != LK_OK) 1200 | return NULL; 1201 | poll = (lk_Poll*)lkP_new(S, &S->polls, svr, name); 1202 | lkP_setpoll(poll); 1203 | poll->slot.handler = h; 1204 | poll->slot.userdata = ud; 1205 | if (lkP_startpoll(poll) != LK_OK) return NULL; 1206 | lk_lock(S->lock); 1207 | if ((poll = (lk_Poll*)lkP_register(S, &poll->slot)) != NULL) { 1208 | poll->slot.next = svr->slots; 1209 | svr->slots = &poll->slot; 1210 | } 1211 | lk_unlock(S->lock); 1212 | if (poll == NULL) { 1213 | lk_log(S, "E[newpoll]", lk_loc("poll '%s' exists"), name); 1214 | return NULL; 1215 | } 1216 | return &poll->slot; 1217 | } 1218 | 1219 | LK_API lk_Slot *lk_slot (lk_State *S, const char *name) { 1220 | lk_Service *svr = lk_self(S); 1221 | lk_Slot *slot = NULL; 1222 | if (S == NULL || svr == NULL || lkP_check(S, "slot", name) != LK_OK) 1223 | return NULL; 1224 | if (strchr(name, '.') == NULL) { 1225 | char qname[LK_MAX_NAMESIZE]; 1226 | lkP_name(qname, svr->slot.name, name); 1227 | slot = lkP_findslotG(S, qname); 1228 | } 1229 | else if (name[0] == '.') { 1230 | char qname[LK_MAX_NAMESIZE]; 1231 | lkP_name(qname, S->root.slot.name, name + 1); 1232 | slot = lkP_findslotG(S, qname); 1233 | } 1234 | if (slot == NULL) 1235 | slot = lkP_findslotG(S, name); 1236 | if (slot == NULL) 1237 | lk_log(S, "E[slot]" lk_loc("slot '%s' not exists"), name); 1238 | return slot; 1239 | } 1240 | 1241 | LK_API void lk_sethook (lk_Slot *slot, lk_Handler *h, void *ud) { 1242 | if (slot == NULL) return; 1243 | lk_lock(slot->service->lock); 1244 | slot->hookf = h; 1245 | slot->hook_ud = ud; 1246 | lk_unlock(slot->service->lock); 1247 | } 1248 | 1249 | static void lkP_callhook (lk_Slot *slot, lk_Slot *sender, lk_Signal *sig) { 1250 | lk_Handler *hookf = slot->hookf; 1251 | void *ud; 1252 | if (hookf == NULL) return; 1253 | lk_lock(slot->service->lock); 1254 | hookf = slot->hookf; 1255 | ud = slot->hook_ud; 1256 | lk_unlock(slot->service->lock); 1257 | if (hookf) { 1258 | lk_Context ctx; 1259 | lk_pushcontext(slot->S, &ctx, slot); 1260 | ctx.userdata = ud; 1261 | lk_try(S, &ctx, hookf(slot->S, sender, sig)); 1262 | lk_popcontext(slot->S, &ctx); 1263 | } 1264 | } 1265 | 1266 | 1267 | /* emit signal */ 1268 | 1269 | static void lkS_active (lk_State *S, lk_Service *svr); 1270 | 1271 | LK_API int lk_emitstring (lk_Slot *slot, unsigned type, const char *s) 1272 | { return slot ? lk_emitdata(slot, type, lk_newstring(slot->S, s)) : LK_ERR; } 1273 | 1274 | static int lkE_srcdeletor (lk_State *S, lk_Slot *sender, lk_Signal *sig) { 1275 | (void)sender; 1276 | lk_lock(S->pool_lock); 1277 | lk_poolfree(&S->sources, sig->source); 1278 | lk_unlock(S->pool_lock); 1279 | return LK_OK; 1280 | } 1281 | 1282 | static lk_SignalNode *lkE_newsignal (lk_State *S, lk_Slot *slot, const lk_Signal *sig) { 1283 | lk_Slot *sender = lk_current(S); 1284 | lk_SignalNode *node; 1285 | lk_Source *src; 1286 | lk_lock(S->pool_lock); 1287 | node = (lk_SignalNode*)lk_poolalloc(S, &S->signals); 1288 | lk_unlock(S->pool_lock); 1289 | node->recipient = slot; 1290 | node->sender = sender; 1291 | node->data = *sig; 1292 | lk_retain(sender->service); 1293 | if (node->data.source == NULL && sender->source != NULL) { 1294 | node->data.source = sender->source; 1295 | sender->source = NULL; 1296 | } 1297 | if ((src = node->data.source) != NULL) { 1298 | lk_retain(src->service); 1299 | lk_usesource(src); 1300 | } 1301 | if (node->data.isdata) lk_usedata(S, (lk_Data*)node->data.data); 1302 | return node; 1303 | } 1304 | 1305 | static void lkE_delsignal (lk_State *S, lk_SignalNode *node) { 1306 | lk_Source *src = node->data.source; 1307 | lk_Service *svr; 1308 | if (node->data.isdata) lk_deldata(S, (lk_Data*)node->data.data); 1309 | if (src != NULL && (svr = src->service) != NULL) { 1310 | lk_freesource(src); 1311 | lk_release(svr); 1312 | } 1313 | if ((svr = node->sender->service) != NULL) 1314 | lk_release(svr); 1315 | lk_lock(S->pool_lock); 1316 | lk_poolfree(&S->signals, node); 1317 | lk_unlock(S->pool_lock); 1318 | } 1319 | 1320 | static int lkE_emitS (lk_Slot *slot, lk_SignalNode *node) { 1321 | lk_Service *svr = slot->service; 1322 | int ret = LK_ERR; 1323 | lk_lock(svr->lock); 1324 | if (svr->slot.S->nthreads != 0 && !lkP_isdead(svr)) { 1325 | lk_Poll *poll = (lk_Poll*)slot; 1326 | if (!lkP_ispoll(slot)) { 1327 | lkQ_enqueue(&svr->signals, node); 1328 | lkS_active(svr->slot.S, svr); 1329 | ret = LK_OK; 1330 | } 1331 | else if (!lkP_isdead(poll)) { 1332 | lk_lock(poll->lock); 1333 | lkQ_enqueue(&poll->signals, node); 1334 | lk_signal(poll->event); 1335 | lk_unlock(poll->lock); 1336 | ret = LK_OK; 1337 | } 1338 | } 1339 | lk_unlock(svr->lock); 1340 | return ret; 1341 | } 1342 | 1343 | static int lkE_filterslot (lk_Table *t, lk_Entry **pe, const char *name) { 1344 | for (;;) { 1345 | lk_Slot *slot; 1346 | const char *slotname; 1347 | if (!lk_nextentry(t, pe)) return 0; 1348 | if ((name == NULL) != lkP_issvr(slot = (lk_Slot*)(*pe)->key)) 1349 | continue; 1350 | if (name == NULL || ((slotname = strchr(slot->name, '.')) != NULL 1351 | && strcmp(slotname+1, name) == 0)) 1352 | return 1; 1353 | } 1354 | } 1355 | 1356 | LK_API void lk_initsource (lk_State *S, lk_Source *src, lk_Handler *h, void *ud) { 1357 | memset(src, 0, sizeof(*src)); 1358 | src->service = lk_self(S); 1359 | src->callback = h; 1360 | src->ud = ud; 1361 | } 1362 | 1363 | LK_API void lk_usesource (lk_Source *src) { 1364 | assert(src && src->service); 1365 | if (src->service == NULL) return; 1366 | lk_lock(src->service->lock); 1367 | ++src->refcount; 1368 | lk_unlock(src->service->lock); 1369 | } 1370 | 1371 | LK_API void lk_freesource (lk_Source *src) { 1372 | lk_Signal sig = LK_SIGNAL; 1373 | lk_State *S; 1374 | unsigned refcount = 0; 1375 | sig.source = src; 1376 | assert(src->service != NULL); 1377 | if (src->service == NULL) return; 1378 | S = src->service->slot.S; 1379 | lk_lock(src->service->lock); 1380 | if (src->refcount >= 1) refcount = --src->refcount; 1381 | lk_unlock(src->service->lock); 1382 | if (refcount == 0 && src->deletor != NULL) { 1383 | lk_Context ctx; 1384 | lk_pushcontext(S, &ctx, &src->service->slot); 1385 | lk_try(S, &ctx, src->deletor(S, NULL, &sig)); 1386 | lk_popcontext(S, &ctx); 1387 | } 1388 | } 1389 | 1390 | LK_API void lk_setcallback (lk_State *S, lk_Handler *h, void *ud) { 1391 | lk_Slot *slot = lk_current(S); 1392 | lk_Source *src; 1393 | if (slot->source != NULL) lk_freesource(slot->source); 1394 | lk_lock(S->pool_lock); 1395 | src = (lk_Source*)lk_poolalloc(S, &S->sources); 1396 | lk_unlock(S->pool_lock); 1397 | lk_initsource(S, src, h, ud); 1398 | src->deletor = lkE_srcdeletor; 1399 | slot->source = src; 1400 | } 1401 | 1402 | LK_API int lk_emit (lk_Slot *slot, const lk_Signal *sig) { 1403 | lk_SignalNode *node; 1404 | assert(slot != NULL); 1405 | if (slot == NULL || sig == NULL) return LK_ERR; 1406 | node = lkE_newsignal(slot->S, slot, sig); 1407 | if (lkE_emitS(slot, node) != LK_OK) { 1408 | lkE_delsignal(slot->S, node); 1409 | return LK_ERR; 1410 | } 1411 | return LK_OK; 1412 | } 1413 | 1414 | LK_API int lk_broadcast (lk_State *S, const char *name, const lk_Signal *sig) { 1415 | lk_Slot *current = lk_current(S); 1416 | lk_SignalNode *node = lkE_newsignal(S, current, sig); 1417 | int count = 0; 1418 | if (node != NULL) { 1419 | lk_Table t; 1420 | lk_Entry *e = NULL; 1421 | lk_lock(S->lock); 1422 | lk_copytable(S, &t, &S->slot_names); 1423 | lk_unlock(S->lock); 1424 | while (lkE_filterslot(&t, &e, name)) { 1425 | lk_Slot *slot = (lk_Slot*)e->key; 1426 | if (sig == NULL || lk_emit(slot, &node->data) == LK_OK) 1427 | ++count; 1428 | } 1429 | lk_freetable(S, &t); 1430 | } 1431 | lkE_delsignal(S, node); 1432 | return count; 1433 | } 1434 | 1435 | LK_API int lk_emitdata (lk_Slot *slot, unsigned type, lk_Data *data) { 1436 | lk_Signal sig = LK_SIGNAL; 1437 | if (slot == NULL) return LK_ERR; 1438 | sig.type = type & LK_TYPE_MASK; 1439 | sig.isdata = 1; 1440 | sig.isack = (type & LK_RESPONSE_TYPE) != 0; 1441 | sig.data = data; 1442 | return lk_emit(slot, &sig); 1443 | } 1444 | 1445 | LK_API int lk_wait (lk_State *S, lk_Signal* sig, int waitms) { 1446 | lk_Poll *poll = (lk_Poll*)lk_current(S); 1447 | lk_Slot *slot = &poll->slot; 1448 | lk_SignalNode *node = NULL; 1449 | if (poll == NULL || !lkP_ispoll(poll)) return LK_ERR; 1450 | if (slot->current) { 1451 | lkP_callhook(slot, slot->current->sender, &slot->current->data); 1452 | lkE_delsignal(S, slot->current); 1453 | slot->current = NULL; 1454 | } 1455 | lk_lock(poll->lock); 1456 | if (sig) lkQ_dequeue(&poll->signals, node); 1457 | while (node == NULL && !lkP_isdead(poll)) { 1458 | int ret = lk_waitevent(&poll->event, &poll->lock, waitms); 1459 | if (sig) lkQ_dequeue(&poll->signals, node); 1460 | if (ret != LK_OK || waitms >= 0) break; 1461 | } 1462 | lk_unlock(poll->lock); 1463 | if (node == NULL) 1464 | return lkP_isdead(poll) ? LK_ERR : LK_TIMEOUT; 1465 | slot->current = node; 1466 | if (sig) *sig = node->data; 1467 | return LK_OK; 1468 | } 1469 | 1470 | 1471 | /* service routines */ 1472 | 1473 | LK_API lk_Service *lk_self (lk_State *S) 1474 | { lk_Slot *slot = lk_current(S); return slot ? slot->service : NULL; } 1475 | 1476 | static int lkS_initsevice (lk_State *S, lk_Service *svr) { 1477 | lkP_setsvr(svr); 1478 | lkP_setactive(svr); 1479 | svr->slot.service = svr; 1480 | svr->slots = &svr->slot; 1481 | lkQ_init(&svr->signals); 1482 | if (!lk_initlock(&svr->lock)) { 1483 | if (svr != &S->root) { 1484 | lk_lock(S->pool_lock); 1485 | lk_poolfree(&S->services, svr); 1486 | lk_unlock(S->pool_lock); 1487 | } 1488 | return LK_ERR; 1489 | } 1490 | return LK_OK; 1491 | } 1492 | 1493 | static void lkS_freepolls (lk_State *S, lk_Service *svr) { 1494 | lk_Slot *slot, **pslots = &svr->slots; 1495 | while (*pslots != NULL) { 1496 | if (lkP_ispoll(slot = *pslots) 1497 | && lkP_delpoll(S, (lk_Poll*)slot) == LK_OK) 1498 | *pslots = slot->next; 1499 | else 1500 | pslots = &slot->next; 1501 | } 1502 | } 1503 | 1504 | static void lkS_freeslotsG (lk_State *S, lk_Service *svr) { 1505 | lk_Slot **pslots, *slot; 1506 | lk_lock(S->lock); 1507 | for (slot = svr->slots; slot != NULL; slot = slot->next) { 1508 | lk_Entry *e = lk_gettable(&S->slot_names, slot->name); 1509 | assert(e && (lk_Slot*)e->key == slot); 1510 | if (e) e->key = NULL; 1511 | } 1512 | lk_unlock(S->lock); 1513 | lk_lock(S->pool_lock); 1514 | for (pslots = &svr->slots; *pslots != NULL;) { 1515 | if (lkP_issvr(slot = *pslots) || lkP_ispoll(slot)) 1516 | pslots = &slot->next; 1517 | else { 1518 | *pslots = slot->next; 1519 | lk_poolfree(&S->slots, slot); 1520 | } 1521 | } 1522 | lk_unlock(S->pool_lock); 1523 | } 1524 | 1525 | static void lkS_release (lk_State *S, lk_Service *svr) { 1526 | lk_lock(S->lock); 1527 | if (!lkP_isweak(svr)) --S->nservices; 1528 | if (S->nservices == 0) { 1529 | lk_lock(S->queue_lock); 1530 | lk_signal(S->queue_event); 1531 | lk_unlock(S->queue_lock); 1532 | } 1533 | lk_unlock(S->lock); 1534 | } 1535 | 1536 | static int lkS_delserviceG (lk_State *S, lk_Service *svr) { 1537 | if (svr->slot.handler) { 1538 | lk_Context ctx; 1539 | lk_pushcontext(S, &ctx, &svr->slot); 1540 | lk_try(S, &ctx, svr->slot.handler(S, &svr->slot, NULL)); 1541 | lk_popcontext(S, &ctx); 1542 | svr->slot.handler = NULL; 1543 | } 1544 | lkS_freepolls(S, svr); 1545 | if (svr->pending != 0) return LK_ERR; 1546 | lkS_freeslotsG(S, svr); 1547 | lkS_release(S, svr); 1548 | lk_freelock(svr->lock); 1549 | assert(lkQ_empty(&svr->signals)); 1550 | if (svr != &S->root) { 1551 | lk_lock(S->pool_lock); 1552 | lk_poolfree(&S->services, svr); 1553 | lk_unlock(S->pool_lock); 1554 | } 1555 | return LK_OK; 1556 | } 1557 | 1558 | static void lkS_active (lk_State *S, lk_Service *svr) { 1559 | if (!lkP_isactive(svr)) { 1560 | lkP_setactive(svr); 1561 | lk_lock(S->queue_lock); 1562 | lkQ_enqueue(&S->main_queue, svr); 1563 | lk_signal(S->queue_event); 1564 | lk_unlock(S->queue_lock); 1565 | } 1566 | } 1567 | 1568 | static void lkS_callslot (lk_State *S, lk_SignalNode *node, lk_Context *ctx) { 1569 | lk_Slot *sender = node->sender; 1570 | lk_Slot *slot = node->recipient; 1571 | lk_Source *src = node->data.source; 1572 | int ret = LK_ERR, isack = node->data.isack; 1573 | ctx->current = slot; 1574 | slot->current = node; 1575 | if (isack) { 1576 | lk_Handler *const refactor = sender->refactor ? 1577 | sender->refactor : sender->service->slot.refactor; 1578 | if (refactor != NULL) 1579 | lk_try(S, ctx, ret = refactor(S, sender, &node->data)); 1580 | } 1581 | if (ret == LK_ERR && src && src->callback 1582 | && (isack || src->force) && src->service == slot->service) 1583 | lk_try(S, ctx, ret = src->callback(S, sender, &node->data)); 1584 | if (ret == LK_ERR && slot->handler != NULL) 1585 | lk_try(S, ctx, slot->handler(S, sender, &node->data)); 1586 | slot->current = NULL; 1587 | lkP_callhook(slot, node->sender, &node->data); 1588 | lkE_delsignal(S, node); 1589 | } 1590 | 1591 | static void lkS_callslotsS (lk_State *S, lk_Service *svr) { 1592 | lk_Context ctx; 1593 | lk_SignalNode *node; 1594 | 1595 | /* fetch all signal */ 1596 | lk_lock(svr->lock); 1597 | lkQ_clear(&svr->signals, node); 1598 | lk_unlock(svr->lock); 1599 | 1600 | /* call signal handler */ 1601 | lk_pushcontext(S, &ctx, &svr->slot); 1602 | while (node != NULL) { 1603 | lk_SignalNode *next = node->next; 1604 | lkS_callslot(S, node, &ctx); 1605 | node = next; 1606 | } 1607 | lk_popcontext(S, &ctx); 1608 | } 1609 | 1610 | static void lkS_dispatchGS (lk_State *S, lk_Service *svr) { 1611 | int should_delete = 0; 1612 | assert(lkP_isactive(svr)); 1613 | lkS_callslotsS(S, svr); 1614 | 1615 | lk_lock(svr->lock); 1616 | if (!lkQ_empty(&svr->signals)) { 1617 | lk_lock(S->queue_lock); 1618 | lkQ_enqueue(&S->main_queue, svr); 1619 | lk_unlock(S->queue_lock); 1620 | } 1621 | else if (lkP_isdead(svr) && svr->pending == 0) 1622 | should_delete = 1; 1623 | else 1624 | lkP_clractive(svr); 1625 | lk_unlock(svr->lock); 1626 | 1627 | if (should_delete && lkS_delserviceG(S, svr) != LK_OK) 1628 | lkP_clractive(svr); 1629 | } 1630 | 1631 | static int lkS_check (lk_State *S, const char *name, lk_Handler *h) { 1632 | if (S == NULL || name == NULL || h == NULL) 1633 | return LK_ERR; 1634 | if (strlen(name) >= LK_MAX_NAMESIZE) { 1635 | lk_log(S, "E[launch]" lk_loc("serivce name '%s' too long"), name); 1636 | return LK_ERR; 1637 | } 1638 | return S->nthreads == 0 ? LK_ERR : LK_OK; 1639 | } 1640 | 1641 | static int lkS_callinit (lk_State *S, lk_Service *svr) { 1642 | lk_Context ctx; 1643 | int ret = LK_ERR; 1644 | lk_Handler *h = svr->slot.handler; 1645 | lk_pushcontext(S, &ctx, &svr->slot); 1646 | lk_try(S, &ctx, ret = h(S, NULL, NULL)); 1647 | lk_popcontext(S, &ctx); 1648 | if (ret < LK_OK || lkP_isdead(svr)) { 1649 | lk_log(S, "E[launch]" lk_loc("serivce '%s' initialize failure"), 1650 | svr->slot.name); 1651 | lkS_delserviceG(S, svr); 1652 | return LK_ERR; 1653 | } 1654 | lk_lock(S->lock); 1655 | ++S->nservices; 1656 | if (S->logger == NULL && strcmp(svr->slot.name, "log") == 0) 1657 | S->logger = &svr->slot; 1658 | if (ret == LK_WEAK) { 1659 | lkP_setweak(svr); 1660 | --S->nservices; 1661 | } 1662 | lk_unlock(S->lock); 1663 | return LK_OK; 1664 | } 1665 | 1666 | static lk_Service *lkS_callinitGS (lk_State *S, lk_Service *svr, lk_Handler *h, void *ud) { 1667 | lk_Signal sig = LK_RESPONSE; 1668 | svr->slot.handler = h; 1669 | svr->slot.userdata = ud; 1670 | if (h && lkS_callinit(S, svr) != LK_OK) 1671 | return NULL; 1672 | sig.data = svr; 1673 | lk_broadcast(S, LK_SLOTNAME_LAUNCH, &sig); 1674 | lk_lock(svr->lock); 1675 | lkP_clractive(svr); 1676 | if (!lkQ_empty(&svr->signals)) 1677 | lkS_active(S, svr); 1678 | lk_unlock(svr->lock); 1679 | return svr; 1680 | } 1681 | 1682 | LK_API lk_Service *lk_launch (lk_State *S, const char *name, lk_Handler *h, void *data) { 1683 | lk_Service *svr; 1684 | if (lkS_check(S, name, h) != LK_OK) return NULL; 1685 | do { 1686 | if ((svr = (lk_Service*)lkP_findslotG(S, name)) != NULL) 1687 | return svr; 1688 | svr = (lk_Service*)lkP_new(S, &S->services, (lk_Service*)name, NULL); 1689 | if (svr == NULL) continue; 1690 | if (lkS_initsevice(S, svr) == LK_OK) { 1691 | lk_lock(S->lock); 1692 | if (!lkP_register(S, &svr->slot)) svr = NULL; 1693 | lk_unlock(S->lock); 1694 | } 1695 | } while (svr == NULL); 1696 | return lkS_callinitGS(S, svr, h, data); 1697 | } 1698 | 1699 | LK_API int lk_retain (lk_Service *svr) { 1700 | int pending; 1701 | if (svr == NULL) return 0; 1702 | lk_lock(svr->lock); 1703 | pending = (int)++svr->pending; 1704 | lk_unlock(svr->lock); 1705 | return pending; 1706 | } 1707 | 1708 | LK_API int lk_release (lk_Service *svr) { 1709 | int pending = 0; 1710 | if (svr == NULL) return 0; 1711 | lk_lock(svr->lock); 1712 | if (svr->pending > 0) pending = (int)--svr->pending; 1713 | if (pending == 0 && lkP_isdead(svr)) lkS_active(svr->slot.S, svr); 1714 | lk_unlock(svr->lock); 1715 | return pending; 1716 | } 1717 | 1718 | 1719 | /* global routines */ 1720 | 1721 | static void lkG_worker(void *ud) { 1722 | lk_State *S = (lk_State*)ud; 1723 | lk_Service *svr; 1724 | lk_lock(S->queue_lock); 1725 | while (S->nservices != 0) { 1726 | lkQ_dequeue(&S->main_queue, svr); 1727 | while (svr == NULL && S->nservices != 0) { 1728 | lk_waitevent(&S->queue_event, &S->queue_lock, -1); 1729 | lkQ_dequeue(&S->main_queue, svr); 1730 | } 1731 | while (svr != NULL) { 1732 | lk_unlock(S->queue_lock); 1733 | lkS_dispatchGS(S, svr); 1734 | lk_lock(S->queue_lock); 1735 | lkQ_dequeue(&S->main_queue, svr); 1736 | } 1737 | } 1738 | lk_signal(S->queue_event); 1739 | lk_unlock(S->queue_lock); 1740 | } 1741 | 1742 | static void *default_allocf (void *ud, void *ptr, size_t size, size_t osize) { 1743 | (void)ud, (void)osize; 1744 | if (size == 0) { free(ptr); return NULL; } 1745 | return realloc(ptr, size); 1746 | } 1747 | 1748 | static void lkG_clearservices (lk_State *S) { 1749 | lk_Entry *e = NULL; 1750 | while (lk_nextentry(&S->slot_names, &e)) { 1751 | lk_Slot *slot = (lk_Slot*)e->key; 1752 | if (lkP_issvr(slot)) { 1753 | int ret; 1754 | assert(lkP_isweak(slot)); 1755 | ret = lkS_delserviceG(S, (lk_Service*)slot); 1756 | assert(ret == LK_OK); 1757 | } 1758 | } 1759 | while (lk_nextentry(&S->config, &e)) 1760 | lk_deldata(S, (lk_Data*)e->key); 1761 | lk_freetable(S, &S->slot_names); 1762 | lk_freetable(S, &S->config); 1763 | } 1764 | 1765 | static int lkG_initstate (lk_State *S, const char *name) { 1766 | name = name ? name : LK_NAME; 1767 | if (lkS_initsevice(S, &S->root) != LK_OK) 1768 | return LK_ERR; 1769 | lk_strcpy(S->root.slot.name, name, LK_MAX_NAMESIZE); 1770 | S->root.slot.S = S; 1771 | S->nthreads = -1; /* no thread and no start */ 1772 | lkQ_init(&S->main_queue); 1773 | lk_initpool(&S->services, sizeof(lk_Service)); 1774 | lk_initpool(&S->slots, sizeof(lk_Slot)); 1775 | lk_initpool(&S->polls, sizeof(lk_Poll)); 1776 | lk_initpool(&S->defers, sizeof(lk_Defer)); 1777 | lk_initpool(&S->signals, sizeof(lk_SignalNode)); 1778 | lk_initpool(&S->sources, sizeof(lk_Source)); 1779 | lk_initpool(&S->smallpieces, LK_SMALLPIECE_LEN); 1780 | lk_inittable(&S->config, sizeof(lk_PtrEntry)); 1781 | lk_inittable(&S->slot_names, sizeof(lk_Entry)); 1782 | lk_settable(S, &S->slot_names, S->root.slot.name); 1783 | return LK_OK; 1784 | } 1785 | 1786 | static void lkG_delstate (lk_State *S) { 1787 | lkG_clearservices(S); 1788 | lk_freepool(S, &S->services); 1789 | lk_freepool(S, &S->slots); 1790 | lk_freepool(S, &S->polls); 1791 | lk_freepool(S, &S->defers); 1792 | lk_freepool(S, &S->signals); 1793 | lk_freepool(S, &S->sources); 1794 | lk_freepool(S, &S->smallpieces); 1795 | lk_freeevent(S->queue_event); 1796 | lk_freetls(S->tls_index); 1797 | lk_freelock(S->config_lock); 1798 | lk_freelock(S->queue_lock); 1799 | lk_freelock(S->lock); 1800 | S->allocf(S->alloc_ud, S, 0, sizeof(lk_State)); 1801 | } 1802 | 1803 | static void lkG_setconfig(lk_State *S, const char *key, const char *value) { 1804 | if (value == NULL) { 1805 | lk_PtrEntry *e = (lk_PtrEntry*)lk_gettable(&S->config, key); 1806 | if (e) lk_deldata(S, (lk_Data*)lk_key(e)), lk_key(e) = NULL; 1807 | } 1808 | else { 1809 | lk_PtrEntry *e = (lk_PtrEntry*)lk_settable(S, &S->config, key); 1810 | size_t ksize = strlen(key); 1811 | size_t vsize = strlen(value); 1812 | char *data; 1813 | if (e->data && strlen((const char*)e->data) >= vsize) { 1814 | memcpy(e->data, value, vsize+1); 1815 | return; 1816 | } 1817 | data = (char*)lk_newdata(S, ksize+vsize+2); 1818 | lk_key(e) = data; 1819 | e->data = data + ksize + 1; 1820 | memcpy(data, key, ksize+1); 1821 | memcpy(e->data, value, vsize+1); 1822 | } 1823 | } 1824 | 1825 | LK_API lk_State *lk_newstate (const char *name, lk_Allocf *allocf, void *ud) { 1826 | enum { TLS, EVT, LCK, QLK, CLK, PLK, TOTAL }; 1827 | lk_Allocf *alloc = allocf ? allocf : default_allocf; 1828 | lk_State *S = (lk_State*)alloc(ud, NULL, sizeof(lk_State), 0); 1829 | unsigned ok = 0; 1830 | if (S == NULL) return NULL; 1831 | memset(S, 0, sizeof(*S)); 1832 | S->allocf = alloc, S->alloc_ud = ud; 1833 | if (lk_inittls(&S->tls_index)) ok |= 1<queue_event)) ok |= 1<lock)) ok |= 1<queue_lock)) ok |= 1<config_lock)) ok |= 1<pool_lock)) ok |= 1<pool_lock); 1841 | if ((ok & (1<config_lock); 1842 | if ((ok & (1<queue_lock); 1843 | if ((ok & (1<lock); 1844 | if ((ok & (1<queue_event); 1845 | if ((ok & (1<tls_index); 1846 | allocf(ud, S, 0, sizeof(lk_State)); 1847 | return NULL; 1848 | } 1849 | 1850 | LK_API void lk_close (lk_State *S) { 1851 | lk_Context *ctx = lk_context(S); 1852 | lk_Service *svr = ctx && ctx->current ? ctx->current->service : NULL; 1853 | if (ctx == NULL && S && S->nservices == 0) 1854 | lkG_delstate(S); 1855 | else if (svr != NULL && !lkP_isdead(svr)) { 1856 | lk_Signal sig = LK_RESPONSE; 1857 | sig.data = svr; 1858 | lk_broadcast(S, LK_SLOTNAME_CLOSE, &sig); 1859 | lk_lock(S->lock); 1860 | if (&svr->slot == S->logger) 1861 | S->logger = NULL; 1862 | lk_unlock(S->lock); 1863 | lk_lock(svr->lock); 1864 | lkP_setdead(svr); 1865 | lkS_active(S, svr); 1866 | lk_unlock(svr->lock); 1867 | } 1868 | } 1869 | 1870 | LK_API int lk_start (lk_State *S, int threads) { 1871 | int i, count = 0; 1872 | if (S == NULL) return 0; 1873 | if (S->nthreads > 0) return S->nthreads; 1874 | lkS_callinitGS(S, &S->root, S->root.slot.handler, S->root.slot.userdata); 1875 | if (S->root.slot.handler == NULL) 1876 | ++S->nservices; 1877 | count = threads <= 0 ? lk_cpucount() : threads; 1878 | for (i = 0; i < count; ++i) { 1879 | if (!lk_initthread(&S->threads[i], lkG_worker, S)) 1880 | break; 1881 | } 1882 | S->nthreads = i; 1883 | lk_unlock(S->lock); 1884 | return i; 1885 | } 1886 | 1887 | LK_API int lk_cpucount (void) { 1888 | #ifdef _WIN32 1889 | SYSTEM_INFO info; 1890 | GetSystemInfo(&info); 1891 | return (int)info.dwNumberOfProcessors; 1892 | #else 1893 | return (int)sysconf(_SC_NPROCESSORS_ONLN); 1894 | #endif 1895 | } 1896 | 1897 | LK_API void lk_waitclose (lk_State *S) { 1898 | if (S != NULL) { 1899 | int i; 1900 | #ifdef _WIN32 1901 | WaitForMultipleObjects(S->nthreads, S->threads, TRUE, INFINITE); 1902 | for (i = 0; i < S->nthreads; ++i) 1903 | lk_freethread(S->threads[i]); 1904 | #else 1905 | for (i = 0; i < S->nthreads; ++i) 1906 | pthread_join(S->threads[i], NULL); 1907 | #endif 1908 | S->nthreads = 0; /* not in win32: we should call CloseHandle() on win32 */ 1909 | } 1910 | } 1911 | 1912 | LK_API char *lk_getconfig (lk_State *S, const char *key) { 1913 | lk_PtrEntry *e; 1914 | char *value = NULL; 1915 | lk_lock(S->config_lock); 1916 | if ((e = (lk_PtrEntry*)lk_gettable(&S->config, key)) != NULL) 1917 | value = (char*)lk_newstring(S, (const char*)e->data); 1918 | lk_unlock(S->config_lock); 1919 | return value; 1920 | } 1921 | 1922 | LK_API void lk_setconfig (lk_State *S, const char *key, const char *value) { 1923 | assert(key != NULL); 1924 | if (key == NULL) return; 1925 | lk_lock(S->config_lock); 1926 | lkG_setconfig(S, key, value); 1927 | lk_unlock(S->config_lock); 1928 | } 1929 | 1930 | LK_API int lk_log (lk_State *S, const char *fmt, ...) { 1931 | va_list l; 1932 | int ret; 1933 | va_start(l, fmt); 1934 | ret = lk_vlog(S, fmt, l); 1935 | va_end(l); 1936 | return ret; 1937 | } 1938 | 1939 | LK_API int lk_vlog (lk_State *S, const char *fmt, va_list l) { 1940 | lk_Slot *logger = S->logger; 1941 | if (logger == NULL) return LK_OK; 1942 | return lk_emitdata(logger, 0, lk_newvfstring(S, fmt, l)); 1943 | } 1944 | 1945 | 1946 | LK_NS_END 1947 | 1948 | #endif 1949 | 1950 | /* win32cc: flags+='-Wextra -s -O3 -mdll -DLOKI_IMPLEMENTATION -std=c90 -pedantic -xc' 1951 | * win32cc: output='loki.dll' 1952 | * unixcc: flags+='-Wextra -s -O3 -fPIC -shared -DLOKI_IMPLEMENTATION -xc' 1953 | * unixcc: output='loki.so' */ 1954 | 1955 | --------------------------------------------------------------------------------