| /* |
| * Wine threading routines using the pthread library |
| * |
| * Copyright 2003 Alexandre Julliard |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #ifdef HAVE_PTHREAD_H |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #include <string.h> |
| #include <sys/types.h> |
| #ifdef HAVE_SYS_MMAN_H |
| #include <sys/mman.h> |
| #endif |
| |
| #include "wine/library.h" |
| #include "wine/pthread.h" |
| |
| static int init_done; |
| |
| #ifndef __i386__ |
| static pthread_key_t teb_key; |
| #endif |
| |
| /*********************************************************************** |
| * init_process |
| * |
| * Initialization for a newly created process. |
| */ |
| static void init_process( const struct wine_pthread_callbacks *callbacks, size_t size ) |
| { |
| init_done = 1; |
| } |
| |
| |
| /*********************************************************************** |
| * init_thread |
| * |
| * Initialization for a newly created thread. |
| */ |
| static void init_thread( struct wine_pthread_thread_info *info ) |
| { |
| /* retrieve the stack info (except for main thread) */ |
| if (init_done) |
| { |
| #ifdef HAVE_PTHREAD_GETATTR_NP |
| pthread_attr_t attr; |
| pthread_getattr_np( pthread_self(), &attr ); |
| pthread_attr_getstack( &attr, &info->stack_base, &info->stack_size ); |
| pthread_attr_destroy( &attr ); |
| #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP) |
| char dummy; |
| info->stack_size = pthread_get_stacksize_np(pthread_self()); |
| info->stack_base = pthread_get_stackaddr_np(pthread_self()); |
| /* if base is too large assume it's the top of the stack instead */ |
| if ((char *)info->stack_base > &dummy) |
| info->stack_base = (char *)info->stack_base - info->stack_size; |
| #else |
| /* assume that the stack allocation is page aligned */ |
| char dummy; |
| size_t page_size = getpagesize(); |
| char *stack_top = (char *)((unsigned long)&dummy & ~(page_size - 1)) + page_size; |
| info->stack_base = stack_top - info->stack_size; |
| #endif |
| } |
| } |
| |
| |
| /*********************************************************************** |
| * create_thread |
| */ |
| static int create_thread( struct wine_pthread_thread_info *info ) |
| { |
| pthread_t id; |
| pthread_attr_t attr; |
| int ret = 0; |
| |
| pthread_attr_init( &attr ); |
| pthread_attr_setstacksize( &attr, info->stack_size ); |
| pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); |
| pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); /* force creating a kernel thread on Solaris */ |
| if (pthread_create( &id, &attr, (void * (*)(void *))info->entry, info )) ret = -1; |
| pthread_attr_destroy( &attr ); |
| return ret; |
| } |
| |
| |
| /*********************************************************************** |
| * init_current_teb |
| * |
| * Set the current TEB for a new thread. |
| */ |
| static void init_current_teb( struct wine_pthread_thread_info *info ) |
| { |
| #ifdef __i386__ |
| /* On the i386, the current thread is in the %fs register */ |
| LDT_ENTRY fs_entry; |
| |
| wine_ldt_set_base( &fs_entry, info->teb_base ); |
| wine_ldt_set_limit( &fs_entry, info->teb_size - 1 ); |
| wine_ldt_set_flags( &fs_entry, WINE_LDT_FLAGS_DATA|WINE_LDT_FLAGS_32BIT ); |
| wine_ldt_init_fs( info->teb_sel, &fs_entry ); |
| #else |
| if (!init_done) /* first thread */ |
| pthread_key_create( &teb_key, NULL ); |
| pthread_setspecific( teb_key, info->teb_base ); |
| #endif |
| |
| /* set pid and tid */ |
| info->pid = getpid(); |
| #ifdef __sun |
| info->tid = pthread_self(); /* this should return the lwp id on solaris */ |
| #else |
| info->tid = gettid(); |
| #endif |
| } |
| |
| |
| /*********************************************************************** |
| * get_current_teb |
| */ |
| static void *get_current_teb(void) |
| { |
| #ifdef __i386__ |
| void *ret; |
| __asm__( ".byte 0x64\n\tmovl 0x18,%0" : "=r" (ret) ); |
| return ret; |
| #else |
| return pthread_getspecific( teb_key ); |
| #endif |
| } |
| |
| |
| /*********************************************************************** |
| * exit_thread |
| */ |
| static void DECLSPEC_NORETURN exit_thread( struct wine_pthread_thread_info *info ) |
| { |
| wine_ldt_free_fs( info->teb_sel ); |
| if (info->teb_size) munmap( info->teb_base, info->teb_size ); |
| pthread_exit( (void *)info->exit_status ); |
| } |
| |
| |
| /*********************************************************************** |
| * abort_thread |
| */ |
| static void DECLSPEC_NORETURN abort_thread( long status ) |
| { |
| pthread_exit( (void *)status ); |
| } |
| |
| |
| /*********************************************************************** |
| * pthread_functions |
| */ |
| const struct wine_pthread_functions pthread_functions = |
| { |
| init_process, |
| init_thread, |
| create_thread, |
| init_current_teb, |
| get_current_teb, |
| exit_thread, |
| abort_thread, |
| pthread_sigmask |
| }; |
| |
| #endif /* HAVE_PTHREAD_H */ |