That same code should be portable to userspace by: - Allocating everything into shared memory, where the shared memory fd replaces the ntsync device fd
- Using an index into a global table of object pointers instead of object fds
- Using futex-based mutexes instead of kernel spinlocks
- Using a futex-based parking/unparking system like parking_lot does
Obviously this breaks if the shared memory is corrupted or if you SIGKILL any process while it's touching it, but for Wine getting that seems acceptable. A kernel driver is clearly better though for this reason.