diff --git a/include/getprop.h b/include/getprop.h index f33df28..b0ed38b 100644 --- a/include/getprop.h +++ b/include/getprop.h @@ -3,13 +3,29 @@ #include "XCB-TRL/xcb_trl.h" #include "prop.h" +#include "queue.h" +typedef struct PropHandler PropHandler; -void PropInit(void); -void PropDestroy(void); -void PropListen(XCBDisplay *display, XCBWindow win, enum PropertyType type); -void PropListenArg(XCBDisplay *display, XCBWindow win, enum PropertyType type, PropArg arg); +struct +PropHandler +{ + CQueue queue; + uint32_t use_threads; + uint32_t thread_length; + uint32_t thread_index; + pthread_t *threads; + GetPropCookie *queue_data; + uint32_t queue_length; +}; + + +PropHandler *PropCreateStatic(void); +void PropInit(PropHandler *handler); +void PropDestroy(PropHandler *handler); +void PropListen(PropHandler *handler, XCBDisplay *display, XCBWindow win, enum PropertyType type); +void PropListenArg(PropHandler *handler, XCBDisplay *display, XCBWindow win, enum PropertyType type, PropArg arg); #endif diff --git a/include/main.h b/include/main.h index 56d4ab0..106465b 100644 --- a/include/main.h +++ b/include/main.h @@ -10,6 +10,7 @@ #include "desktop.h" #include "monitor.h" #include "prop.h" +#include "getprop.h" #include "x.h" #include "../tools/util.h" @@ -111,6 +112,7 @@ struct WM Monitor *mons; /* Monitors */ XCBKeySymbols *syms; /* keysym alloc */ char *wmname; /* WM_NAME */ + PropHandler *handler; /* Prop Handler */ pthread_mutex_t mutex; /* Mutex for main thread */ uint8_t restart; /* Restart flag */ diff --git a/src/client.c b/src/client.c index 2df1877..7406caf 100644 --- a/src/client.c +++ b/src/client.c @@ -1362,10 +1362,10 @@ setclientwtype(Client *c, XCBAtom atom, u8 state) PropArg arg; arg.ui[0] = atom; if(state) - { PropListenArg(_wm.dpy, c->win, PropSetWtype, arg); + { PropListenArg(_wm.handler, _wm.dpy, c->win, PropSetWtype, arg); } else - { PropListenArg(_wm.dpy, c->win, PropUnsetWtype, arg); + { PropListenArg(_wm.handler, _wm.dpy, c->win, PropUnsetWtype, arg); } } @@ -1375,10 +1375,10 @@ setclientnetstate(Client *c, XCBAtom atom, u8 state) PropArg arg; arg.ui[0] = atom; if(state) - { PropListenArg(_wm.dpy, c->win, PropSetWState, arg); + { PropListenArg(_wm.handler, _wm.dpy, c->win, PropSetWState, arg); } else - { PropListenArg(_wm.dpy, c->win, PropUnsetWState, arg); + { PropListenArg(_wm.handler, _wm.dpy, c->win, PropUnsetWState, arg); } } diff --git a/src/events.c b/src/events.c index 990e2f7..2ff863f 100644 --- a/src/events.c +++ b/src/events.c @@ -827,7 +827,7 @@ maprequest(XCBGenericEvent *event) /* map window first (illusion of responsiveness) */ XCBMapWindow(_wm.dpy, win); - PropListen(_wm.dpy, win, PropManage); + PropListen(_wm.handler, _wm.dpy, win, PropManage); if(sync) { XCBFlush(_wm.dpy); @@ -971,7 +971,7 @@ destroynotify(XCBGenericEvent *event) u8 sync = 0; - PropListen(_wm.dpy, win, PropUnmanage); + PropListen(_wm.handler, _wm.dpy, win, PropUnmanage); if(sync) { XCBFlush(_wm.dpy); @@ -1043,7 +1043,7 @@ unmapnotify(XCBGenericEvent *event) u8 sync = 0; - PropListen(_wm.dpy, win, PropUnmanage); + PropListen(_wm.handler, _wm.dpy, win, PropUnmanage); if(sync) { XCBFlush(_wm.dpy); @@ -1402,22 +1402,22 @@ propertynotify(XCBGenericEvent *event) switch(atom) { case XCB_ATOM_WM_TRANSIENT_FOR: - PropListen(_wm.dpy, win, PropTransient); + PropListen(_wm.handler, _wm.dpy, win, PropTransient); break; case XCB_ATOM_WM_HINTS: - PropListen(_wm.dpy, win, PropWMHints); + PropListen(_wm.handler, _wm.dpy, win, PropWMHints); break; case XCB_ATOM_WM_NORMAL_HINTS: - PropListen(_wm.dpy, win, PropSizeHints); + PropListen(_wm.handler, _wm.dpy, win, PropSizeHints); break; case XCB_ATOM_WM_NAME: - PropListen(_wm.dpy, win, PropWMName); + PropListen(_wm.handler, _wm.dpy, win, PropWMName); break; case XCB_ATOM_WM_ICON_NAME: /* ignore */ break; case XCB_ATOM_WM_CLASS: - PropListen(_wm.dpy, win, PropWMClass); + PropListen(_wm.handler, _wm.dpy, win, PropWMClass); break; case XCB_ATOM_WM_CLIENT_MACHINE: /* ignore */ @@ -1425,34 +1425,34 @@ propertynotify(XCBGenericEvent *event) default: /* other atoms */ if(atom == motifatom) - { PropListen(_wm.dpy, win, PropMotifHints); + { PropListen(_wm.handler, _wm.dpy, win, PropMotifHints); } else if(atom == netatom[NetWMName]) - { PropListen(_wm.dpy, win, PropNetWMName); + { PropListen(_wm.handler, _wm.dpy, win, PropNetWMName); } else if(atom == netatom[NetWMWindowType]) - { PropListen(_wm.dpy, win, PropWindowType); + { PropListen(_wm.handler, _wm.dpy, win, PropWindowType); } else if(atom == netatom[NetWMState]) - { PropListen(_wm.dpy, win, PropWindowState); + { PropListen(_wm.handler, _wm.dpy, win, PropWindowState); } else if(atom == wmatom[WMProtocols]) - { PropListen(_wm.dpy, win, PropWMProtocol); + { PropListen(_wm.handler, _wm.dpy, win, PropWMProtocol); } else if(atom == netatom[NetWMStrut]) - { PropListen(_wm.dpy, win, PropStrut); + { PropListen(_wm.handler, _wm.dpy, win, PropStrut); } else if(atom == netatom[NetWMStrutPartial]) - { PropListen(_wm.dpy, win, PropStrutp); + { PropListen(_wm.handler, _wm.dpy, win, PropStrutp); } else if(atom == netatom[NetWMPid]) - { PropListen(_wm.dpy, win, PropPid); + { PropListen(_wm.handler, _wm.dpy, win, PropPid); } else if(atom == netatom[NetWMIcon]) - { PropListen(_wm.dpy, win, PropIcon); + { PropListen(_wm.handler, _wm.dpy, win, PropIcon); } else if(atom == motifatom) - { PropListen(_wm.dpy, win, PropMotifHints); + { PropListen(_wm.handler, _wm.dpy, win, PropMotifHints); } break; } diff --git a/src/getprop.c b/src/getprop.c index ed1805b..a6dc45d 100644 --- a/src/getprop.c +++ b/src/getprop.c @@ -12,38 +12,21 @@ #include "prop.h" #include "main.h" -#define QUEUE_SIZE 256 -/* realistically you wont ever need more than 64 as most of these threads are just waiting for data. */ -#define MAX_THREADS 64 - -typedef struct ThreadHandler ThreadHandler; +extern WM _wm; +PropHandler __handler__; -struct -ThreadHandler -{ - CQueue queue; - uint32_t use_threads; - pthread_t threads[MAX_THREADS]; - GetPropCookie queue_data[QUEUE_SIZE]; -}; +/* forward declartions */ +static void PropInitThreads(PropHandler *handler); +static void PropInitQueue(PropHandler *handler); -static ThreadHandler __threads; - -extern WM _wm; -/* These dont require mutex for the following reasons: - * - They are stack allocated. - * - They are set during setup() before propertynotify is intialized. - * - They are never changed afterwards. - * - These threads are killed before exit, to prevent the stack from de-initializing these variables. - */ -extern XCBAtom netatom[NetLast]; -extern XCBAtom wmatom[WMLast]; -extern XCBAtom motifatom; void * -Worker(void *x) +Worker( + void *x + ) { + PropHandler *handler = x; int screen; XCBDisplay *display = XCBOpenDisplay(NULL, &screen); GetPropCookie cookie = { .win = 0, .type = 0 }; @@ -55,11 +38,11 @@ Worker(void *x) while(cookie.type != PropExitThread) { /* wait for stuff to happen */ - pthread_mutex_lock(&__threads.queue.condmutex); - pthread_cond_wait(&__threads.queue.cond, &__threads.queue.condmutex); - pthread_mutex_unlock(&__threads.queue.condmutex); + pthread_mutex_lock(&handler->queue.condmutex); + pthread_cond_wait(&handler->queue.cond, &handler->queue.condmutex); + pthread_mutex_unlock(&handler->queue.condmutex); /* grab if any item */ - CQueuePop(&__threads.queue, &cookie); + CQueuePop(&handler->queue, &cookie); if(cookie.win) { @@ -73,7 +56,9 @@ Worker(void *x) static int -CreateWorkerAttr(pthread_attr_t *attr) +CreateWorkerAttr( + pthread_attr_t *attr + ) { int status = 0; status = pthread_attr_init(attr); @@ -99,7 +84,10 @@ CreateWorkerAttr(pthread_attr_t *attr) * RETURN: pthread_create() return values. */ static int -CreateWorker(pthread_t *id_return) +CreateWorker( + PropHandler *handler, + pthread_t *id_return + ) { int ret; pthread_attr_t attr; @@ -107,104 +95,175 @@ CreateWorker(pthread_t *id_return) if(ret) { return ret; } - ret = pthread_create(id_return, &attr, Worker, NULL); + ret = pthread_create(id_return, &attr, Worker, handler); pthread_attr_destroy(&attr); return ret; } -static uint32_t -PropGetThreadCount(void) -{ - static uint32_t threads = 0; - if(threads) - { return threads; - } - /* default just use 4 */ - uint32_t aloc_threads = 4; - long cores = sysconf(_SC_NPROCESSORS_ONLN); - if(cores > aloc_threads) - { aloc_threads = cores * 2; /* 2 threads per core */ - } - if(aloc_threads > MAX_THREADS) - { aloc_threads = MAX_THREADS; - } - threads = aloc_threads; - return aloc_threads; -} - static void -PropCreateWorkers(uint32_t threads) +PropCreateWorkers( + PropHandler *handler, + uint32_t workers + ) { - int32_t i; - for(i = 0; i < threads; ++i) - { CreateWorker(&__threads.threads[i]); + int64_t i; + int status; + for(i = 0; i < workers; ++i) + { + status = CreateWorker(handler, &handler->threads[i]); + /* Restart postion, if failed */ + if(status) + { + Debug("Failed to create Worker thread, pthread status [%d]", status); + --i; + --workers; + } } } -static void -PropDestroyWorkers(uint32_t threads) +void +PropDestroyWorkers( + PropHandler *handler + ) { - while(!CQueueIsEmpty(&__threads.queue)) - { CQueuePop(&__threads.queue, NULL); + while(!CQueueIsEmpty(&handler->queue)) + { CQueuePop(&handler->queue, NULL); } - while(!CQueueIsFull(&__threads.queue)) - { PropListen(NULL, 0, PropExitThread); + while(!CQueueIsFull(&handler->queue)) + { PropListen(handler, NULL, 0, PropExitThread); } /* Wakup any threads (if they didnt wakup already) */ - pthread_cond_broadcast(&__threads.queue.cond); + pthread_cond_broadcast(&handler->queue.cond); uint32_t i; - for(i = 0; i < threads; ++i) - { pthread_join(__threads.threads[i], NULL); + for(i = 0; i < handler->thread_length; ++i) + { pthread_join(handler->threads[i], NULL); } } + +PropHandler * +PropCreateStatic( + void + ) +{ return &__handler__; +} + void -PropInit(void) +PropInit( + PropHandler *handler + ) { /* Make clean */ - memset(&__threads, 0, sizeof(ThreadHandler)); + memset(handler, 0, sizeof(PropHandler)); if(!_wm.use_threads) { - __threads.use_threads = 0; + handler->use_threads = 0; + return; + } + + PropInitQueue(handler); + + if(!handler->queue_data) + { + handler->use_threads = 0; return; } - uint8_t ret = CQueueCreate((void **)&__threads.queue_data, QUEUE_SIZE, sizeof(GetPropCookie), &__threads.queue); - __threads.use_threads = !ret; - if(!ret) - { PropCreateWorkers(PropGetThreadCount()); + + u8 ret = CQueueCreate((void **)handler->queue_data, handler->queue_length, sizeof(GetPropCookie), &handler->queue); + + handler->use_threads = !ret; + + if(handler->use_threads) + { + PropInitThreads(handler); + if(handler->thread_length) + { PropCreateWorkers(handler, handler->thread_length); + } + else + { + PropDestroy(handler); + handler->use_threads = 0; + } } } -void -PropDestroy(void) +static void +PropInitThreads( + PropHandler *handler + ) { - if(!_wm.use_threads) - { return; + /* default just use 4 */ + u32 aloc_threads = 4; + u32 BASE_LINE_MAX = 500; /* Anything past 500 threads wouldnt be particularly necessary. */ + long cores = sysconf(_SC_NPROCESSORS_ONLN); + if(cores > aloc_threads) + { aloc_threads = cores * 2; /* 2 threads per core */ } - if(__threads.use_threads) - { PropDestroyWorkers(PropGetThreadCount()); + if(aloc_threads > BASE_LINE_MAX) + { aloc_threads = BASE_LINE_MAX; } - CQueueDestroy(&__threads.queue); + + void *threads = NULL; + size_t alloc_size = aloc_threads * sizeof(*handler->threads); + while(!threads && alloc_size) + { + threads = malloc(alloc_size); + alloc_size /= 2; + } + handler->thread_length = aloc_threads; + handler->threads = threads; +} + +void +PropInitQueue( + PropHandler *handler + ) +{ + const u32 DEFAULT_SIZE = 256; + const size_t size = sizeof(GetPropCookie) * DEFAULT_SIZE; + handler->queue_data = malloc(size); + handler->queue_length = DEFAULT_SIZE; +} + +void +PropDestroy( + PropHandler *handler + ) +{ + if(handler->use_threads) + { PropDestroyWorkers(handler); + } + CQueueDestroy(&handler->queue); + free(handler->queue_data); + free(handler->threads); } void -PropListen(XCBDisplay *display, XCBWindow win, enum PropertyType type) +PropListen( + PropHandler *handler, + XCBDisplay *display, + XCBWindow win, + enum PropertyType type + ) { PropArg arg = {0}; - PropListenArg(display, win, type, arg); + PropListenArg(handler, display, win, type, arg); } void -PropListenArg(XCBDisplay *display, XCBWindow win, enum PropertyType type, PropArg arg) +PropListenArg( + PropHandler *handler, + XCBDisplay *display, + XCBWindow win, + enum PropertyType type, + PropArg arg + ) { - int full = CQueueIsFull(&__threads.queue); - int usethreads = __threads.use_threads && _wm.use_threads; - GetPropCookie cookie; - cookie.win = win; - cookie.type = type; - cookie.arg = arg; + int full = CQueueIsFull(&handler->queue); + int usethreads = handler->use_threads && _wm.use_threads; + GetPropCookie cookie = { .win = win, .type = type, .arg = arg }; if(usethreads && !full) - { CQueueAdd(&__threads.queue, (void *)&cookie); + { CQueueAdd(&handler->queue, (void *)&cookie); } else { /* single thread operation */ @@ -213,5 +272,3 @@ PropListenArg(XCBDisplay *display, XCBWindow win, enum PropertyType type, PropAr } } - - diff --git a/src/main.c b/src/main.c index d4f7e4c..75e877d 100644 --- a/src/main.c +++ b/src/main.c @@ -313,7 +313,7 @@ void cleanup(void) { savesession(); - PropDestroy(); + PropDestroy(_wm.handler); if(!_wm.dpy) { /* sometimes due to our own lack of competence we can call quit twice and segfault here */ @@ -758,7 +758,7 @@ restoremonsession(char *buff, u16 len) { XCBWindow win = pullm->bar->win; unmanage(pullm->bar, 0); - PropListen(_wm.dpy, win, PropManage); + PropListen(_wm.handler, _wm.dpy, win, PropManage); } setupbar(pullm, b); } @@ -1148,6 +1148,8 @@ setup(void) setupcursors(); setupcfg(); setupwm(); + /* Setup prop handler */ + PropInit(_wm.handler); /* finds any monitor's */ updategeom(); updatedesktopnum(); @@ -1416,7 +1418,6 @@ startup(void) setupsys(); startupwm(); checkotherwm(); - PropInit(); atexit(exithandler); #ifndef Debug XCBSetErrorHandler(xerror); @@ -1450,6 +1451,7 @@ startupwm(void) } DIECAT("FATAL: Cannot Connect to X Server. [%s]", display); } + _wm.handler = PropCreateStatic(); /* This allows for execvp and exec to only spawn process on the specified display rather than the default varaibles */ if(display) { setenv("DISPLAY", display, 1);