(this is for review by people who know better than me :)
[ some functions are replaced here by lower case versions because this code is not part of dlls/ntdll/thread.c. consequently, we have to dlopen() that library, and dlsym() the functions we need. if this ever makes it into Wine, this will go away ]
/*********************************************************************** * adopt_thread - take an existing linux thread (of some kind) and make win32 aware of it */ int WineAdoptThread (void) { TEB *teb; void *addr; ULONG size; struct wine_pthread_thread_info thread_info; struct debug_info debug_info; int request_fd; int tid;
debug_info.str_pos = debug_info.strings; debug_info.out_pos = debug_info.output;
teb = shared_alloc_teb( &size ); teb->tibflags = TEBF_WIN32; teb->exit_code = STILL_ACTIVE; teb->request_fd = -1; teb->reply_fd = -1; teb->wait_fd[0] = -1; teb->wait_fd[1] = -1; teb->debug_info = &debug_info; teb->htask16 = NtCurrentTeb()->htask16;
thread_info.stack_base = NULL; thread_info.stack_size = 0; thread_info.teb_base = teb; thread_info.teb_size = size; thread_info.teb_sel = teb->teb_sel;
wine_pthread_init_current_teb( &thread_info );
SERVER_START_REQ( new_thread ) { size_t rsz = sizeof (*req);
req->suspend = FALSE; req->inherit = 0; /* FIXME */
pthread_mutex_lock (&proxy_thread_lock);
if (write (proxy_pipe_request[1], &__req, sizeof (__req)) != sizeof (__req) || read (proxy_pipe_status[0], &__req, sizeof (__req)) != sizeof (__req) || read (proxy_pipe_status[0], &request_fd, sizeof (request_fd)) != sizeof (request_fd)) { fst_error ("cannot read/write proxy thread request (%s)", strerror (errno)); }
pthread_mutex_unlock (&proxy_thread_lock); if (request_fd >= 0) { tid = reply->tid; } fst_error ("request FD for adopted thread is %d\n", request_fd);
} SERVER_END_REQ;
if (request_fd < 0) goto error;
teb->ClientId.UniqueProcess = (HANDLE)GetCurrentProcessId(); teb->ClientId.UniqueThread = (HANDLE)tid; teb->request_fd = request_fd;
/* don't bother with signal initialization since we redirect all signals back to linux anyway. */ /* SIGNAL_Init(); */
/* server_*/ init_thread( thread_info.pid, thread_info.tid, NULL ); wine_pthread_init_thread( &thread_info );
/* create a memory view for the TEB */ ntallocatevm ( GetCurrentProcess(), &addr, teb, &size, MEM_SYSTEM, PAGE_EXECUTE_READWRITE );
/* allocate a memory view for the stack */ size = thread_info.stack_size; ntallocatevm ( GetCurrentProcess(), &teb->DeallocationStack, thread_info.stack_base, &size, MEM_SYSTEM, PAGE_EXECUTE_READWRITE ); /* limit is lower than base since the stack grows down */ teb->Tib.StackBase = (char *)thread_info.stack_base + thread_info.stack_size; teb->Tib.StackLimit = thread_info.stack_base;
/* setup the guard page */ size = 1; ntprotectvm ( GetCurrentProcess(), &teb->DeallocationStack, &size, PAGE_EXECUTE_READWRITE | PAGE_GUARD, NULL );
acquirepeblock (); InsertHeadList( &tls_link_head, &teb->TlsLinks ); releasepeblock ();
fprintf (stderr, "thread adopted\n");
return 0;
error: shared_free_teb (teb); /* XXX deallocate VM */ /* close thread handle */ close (request_fd); return -1; }
I should note for observers that this is part of work Paul is doing to allow WineLib to be initialized on the fly, so libwine can be dlopened and used directly.
On Thu, 01 Apr 2004 00:25:45 -0500, Paul Davis wrote:
/***********************************************************************
- adopt_thread - take an existing linux thread (of some kind) and make win32 aware of it
*/ int WineAdoptThread (void) {
Which is it - WineAdoptThread or wine_adopt_thread? :) As it's a Wine API it should be the latter, I think.
TEB *teb; void *addr; ULONG size; struct wine_pthread_thread_info thread_info; struct debug_info debug_info; int request_fd; int tid; debug_info.str_pos = debug_info.strings; debug_info.out_pos = debug_info.output; teb = shared_alloc_teb( &size ); teb->tibflags = TEBF_WIN32; teb->exit_code = STILL_ACTIVE; teb->request_fd = -1; teb->reply_fd = -1; teb->wait_fd[0] = -1; teb->wait_fd[1] = -1; teb->debug_info = &debug_info;
teb->htask16 = NtCurrentTeb()->htask16;
This line seems to be (a) a no-op, and (b) NtCurrentTeb requires %fs to be initialized which I thought happened in wine_pthread_init_current_teb()? How does this part work?
thread_info.stack_base = NULL; thread_info.stack_size = 0; thread_info.teb_base = teb; thread_info.teb_size = size; thread_info.teb_sel = teb->teb_sel; wine_pthread_init_current_teb( &thread_info ); SERVER_START_REQ( new_thread ) { size_t rsz = sizeof (*req); req->suspend = FALSE; req->inherit = 0; /* FIXME */
pthread_mutex_lock (&proxy_thread_lock);
if (write (proxy_pipe_request[1], &__req, sizeof (__req)) != sizeof (__req) || read (proxy_pipe_status[0], &__req, sizeof (__req)) != sizeof (__req) || read (proxy_pipe_status[0], &request_fd, sizeof (request_fd)) != sizeof (request_fd)) { fst_error ("cannot read/write proxy thread request (%s)", strerror (errno)); }
I guess this means you went for the butler thread approach?
pthread_mutex_unlock (&proxy_thread_lock);
if (request_fd >= 0) { tid = reply->tid; }
fst_error ("request FD for adopted thread is %d\n", request_fd);
} SERVER_END_REQ; if (request_fd < 0) goto error; teb->ClientId.UniqueProcess = (HANDLE)GetCurrentProcessId(); teb->ClientId.UniqueThread = (HANDLE)tid; teb->request_fd = request_fd; /* don't bother with signal initialization since we redirect all signals back to linux anyway. */ /* SIGNAL_Init(); */ /* server_*/ init_thread( thread_info.pid, thread_info.tid, NULL ); wine_pthread_init_thread( &thread_info ); /* create a memory view for the TEB */ ntallocatevm ( GetCurrentProcess(), &addr, teb, &size, MEM_SYSTEM, PAGE_EXECUTE_READWRITE ); /* allocate a memory view for the stack */ size = thread_info.stack_size; ntallocatevm ( GetCurrentProcess(), &teb->DeallocationStack, thread_info.stack_base, &size, MEM_SYSTEM, PAGE_EXECUTE_READWRITE ); /* limit is lower than base since the stack grows down */ teb->Tib.StackBase = (char *)thread_info.stack_base + thread_info.stack_size; teb->Tib.StackLimit = thread_info.stack_base; /* setup the guard page */ size = 1; ntprotectvm ( GetCurrentProcess(), &teb->DeallocationStack, &size, PAGE_EXECUTE_READWRITE | PAGE_GUARD, NULL ); acquirepeblock (); InsertHeadList( &tls_link_head, &teb->TlsLinks ); releasepeblock (); fprintf (stderr, "thread adopted\n"); return 0;
error: shared_free_teb (teb); /* XXX deallocate VM */ /* close thread handle */ close (request_fd); return -1; }