this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update workq_kernreturn for some API changes

First, libpthread now expects `workq_kernreturn` not to return when called with `WQOPS_THREAD_RETURN`. We have to resume the thread and give it a special value for `nkevents` that tells it to kill itself.

Next, Apple has reworked the priority system into a "Quality of Service" (QoS) system. Unfortunately, since we're building libdispatch with a few different settings than Apple's configuration, it seems that they've forgotten to correctly set QoS in some cases, so we have to it for them so that libdispatch doesn't blow up.

Finally, `WQOPS_SETUP_DISPATCH` has been stubbed so libdispatch thinks everything is fine. In the future, we might want to do something with the values it passes in, but for now we have nothing to do with them.

+65 -24
+65 -24
src/kernel/emulation/linux/bsdthread/workq_kernreturn.c
··· 13 13 #include <os/lock.h> 14 14 #include <elfcalls/threads.h> 15 15 16 + #define __PTHREAD_EXPOSE_INTERNALS__ 1 17 + #include <pthread/priority_private.h> 18 + 19 + // much easier to include than libpthread's `internal.h` 20 + #include "../../../../libelfloader/native/dthreads.h" 21 + 16 22 #define WQ_MAX_THREADS 64 17 23 18 24 #define WQOPS_QUEUE_ADD 1 ··· 24 30 #define WQOPS_QUEUE_REQTHREADS 0x20 25 31 #define WQOPS_QUEUE_REQTHREADS2 0x30 26 32 #define WQOPS_SET_EVENT_MANAGER_PRIORITY 0x80 33 + #define WQOPS_SETUP_DISPATCH 0x400 27 34 28 35 // Flags for the newly spawned thread: 29 36 // WQ_FLAG_THREAD_NEWSPI 30 37 // WQ_FLAG_THREAD_REUSE (0 if thread is newly spawned) 31 38 // item is unused in "new SPI" 32 39 33 - #define WQ_FLAG_THREAD_PRIOMASK 0x0000ffff 34 - #define WQ_FLAG_THREAD_OVERCOMMIT 0x00010000 35 - #define WQ_FLAG_THREAD_REUSE 0x00020000 36 - #define WQ_FLAG_THREAD_NEWSPI 0x00040000 37 - #define WQ_FLAG_THREAD_KEVENT 0x00080000 /* thread is response to kevent req */ 40 + #define WQ_FLAG_THREAD_PRIO_SCHED 0x00008000 41 + #define WQ_FLAG_THREAD_PRIO_QOS 0x00004000 42 + #define WQ_FLAG_THREAD_PRIO_MASK 0x00000fff 43 + 44 + #define WQ_FLAG_THREAD_OVERCOMMIT 0x00010000 45 + #define WQ_FLAG_THREAD_REUSE 0x00020000 46 + #define WQ_FLAG_THREAD_NEWSPI 0x00040000 47 + #define WQ_FLAG_THREAD_KEVENT 0x00080000 48 + #define WQ_FLAG_THREAD_EVENT_MANAGER 0x00100000 49 + #define WQ_FLAG_THREAD_TSD_BASE_SET 0x00200000 50 + #define WQ_FLAG_THREAD_WORKLOOP 0x00400000 51 + #define WQ_FLAG_THREAD_OUTSIDEQOS 0x00800000 52 + 53 + #define WORKQ_EXIT_THREAD_NKEVENT (-1) 38 54 39 55 static int workq_sem = WQ_MAX_THREADS; // max 64 threads in use 40 56 static os_unfair_lock workq_parked_lock = OS_UNFAIR_LOCK_INIT; ··· 50 66 struct wq_kevent_data* event; 51 67 TAILQ_ENTRY(parked_thread) entries; 52 68 }; 53 - 54 - extern void* pthread_get_stackaddr_np(void* pth); 55 69 56 70 static void list_add(struct parked_thread* head, struct parked_thread* item); 57 71 static void list_remove(struct parked_thread* head, struct parked_thread* item); ··· 111 125 112 126 int thread_self; 113 127 struct parked_thread me; 114 - void* pth; 128 + dthread_t dthread; 129 + bool terminating = false; 115 130 116 131 os_unfair_lock_lock(&workq_parked_lock); 117 132 ··· 144 159 145 160 os_unfair_lock_unlock(&workq_parked_lock); 146 161 147 - // Let the thread terminate (libc will call pthread_exit) 148 - return 0; 162 + terminating = true; 163 + 164 + // resume the thread; it'll call `_pthread_wqthread_exit` and kill itself 165 + goto resume_thread; 149 166 } 150 167 wakeup: 151 168 152 169 // reset stack and call entry point again with WQ_FLAG_THREAD_REUSE 153 - thread_self = thread_self_trap(); 154 - pth = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF); 170 + thread_self = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_MACH_THREAD_SELF); 171 + dthread = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF); 155 172 156 173 // __simple_printf("Thread %d woken up, prio=%d\n", thread_self, me.flags & WQ_FLAG_THREAD_PRIOMASK); 157 174 158 175 if (me.event) 159 176 wq_event_pending = me.event; 177 + 178 + resume_thread: 179 + 160 180 #ifdef __x86_64__ 181 + // arguments are in rdi, rsi, rdx, rcx, r8, r9 161 182 __asm__ __volatile__ ( 162 183 // "int3\n" 163 184 "movq %%rbx, %%r8\n" // 5th argument 164 185 "movl %5, %%r9d\n" // 6th argument 165 186 "movq %0, %%rsp\n" 166 - "movq %%rsp, %%rdx\n" // 3rd argument 167 - "xorq %%rcx, %%rcx\n" // 4th argument 168 187 "subq $32, %%rsp\n" 169 188 "jmpq *%2\n" 170 - :: "D" (pth), "S" (thread_self), "a" (wqueue_entry_point), 171 - "b" (me.flags | WQ_FLAG_THREAD_REUSE), "c" (me.event ? me.event->events : NULL), 172 - "r" (me.event ? me.event->nevents : 0) 189 + :: "D" (dthread), "S" (thread_self), "a" (wqueue_entry_point), 190 + "b" (me.flags | WQ_FLAG_THREAD_REUSE), "c" ((!terminating && me.event) ? me.event->events : NULL), 191 + "r" (terminating ? WORKQ_EXIT_THREAD_NKEVENT : (me.event ? me.event->nevents : 0)), "d" (dthread->stackbottom) 173 192 ); 174 193 #elif defined(__i386__) 175 194 // Arguments are in eax, ebx, ecx, edx, edi, esi 176 195 __asm__ __volatile__ ( 177 196 "movl %0, %%esp\n" 178 - "movl %0, %%ecx\n" 179 - "xorl %%edx, %%edx\n" 180 197 "subl $32, %%esp\n" 181 198 "jmpl *%2\n" 182 - :: "a" (pth), "b" (thread_self), "S" (wqueue_entry_point), 199 + :: "a" (dthread), "b" (thread_self), "S" (wqueue_entry_point), 183 200 "D" (me.flags | WQ_FLAG_THREAD_REUSE), "d" (me.event ? me.event->events : NULL), 184 - "S" (me.event ? me.event->nevents : 0) 201 + "S" (terminating ? WORKQ_EXIT_THREAD_NKEVENT : (me.event ? me.event->nevents : 0)), "c" (dthread->stackbottom) 185 202 ); 186 203 #else 187 204 # error Missing assembly! ··· 203 220 { 204 221 // affinity contains thread count 205 222 206 - int i, flags; 223 + int i, flags, qos = _pthread_priority_thread_qos(prio); 207 224 208 - flags = WQ_FLAG_THREAD_NEWSPI; 209 - flags |= prio & (WQ_FLAG_THREAD_PRIOMASK | WQ_FLAG_THREAD_OVERCOMMIT); //priority_to_class(prio); 225 + // Apple has created a mess of QoS in libdispatch and sometimes libdispatch doesn't set QoS properly 226 + // when building with configurations Apple (apparently) hasn't tested 227 + // 228 + // we only do this is a hack/workaround until we improve our workqueue/kqueue support to use libdispatch's new SPIs 229 + if (qos == 0) { 230 + qos = 4; // default libdispatch QoS 231 + } 232 + 233 + flags = WQ_FLAG_THREAD_NEWSPI | qos | WQ_FLAG_THREAD_PRIO_QOS; 234 + 235 + if (prio & _PTHREAD_PRIORITY_OVERCOMMIT_FLAG) { 236 + flags |= WQ_FLAG_THREAD_OVERCOMMIT; 237 + } 210 238 211 239 if (wq_event != NULL) 212 240 flags |= WQ_FLAG_THREAD_KEVENT; ··· 265 293 } 266 294 return 0; 267 295 } 296 + case WQOPS_SETUP_DISPATCH: { 297 + // TODO: actually do something with the info passed in 298 + // XNU just tacks it onto the process its called for and uses it later when someone asks for it 299 + 300 + /* 301 + struct workq_dispatch_config cfg = {0}; 302 + 303 + // `affinity` contains the size of the config structure passed in 304 + memcpy(&cfg, item, (affinity < sizeof(struct workq_dispatch_config)) ? affinity : sizeof(struct workq_dispatch_config)); 305 + */ 306 + 307 + return 0; 308 + } break; 268 309 default: 269 310 return -ENOTSUP; 270 311 }