diff options
-rw-r--r-- | winsup/cygwin/ChangeLog | 56 | ||||
-rw-r--r-- | winsup/cygwin/fhandler.h | 3 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_proc.cc | 15 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_process.cc | 25 | ||||
-rw-r--r-- | winsup/cygwin/path.cc | 8 | ||||
-rw-r--r-- | winsup/cygwin/path.h | 2 | ||||
-rw-r--r-- | winsup/cygwin/pinfo.cc | 3 | ||||
-rw-r--r-- | winsup/cygwin/syscalls.cc | 144 | ||||
-rw-r--r-- | winsup/cygwin/thread.cc | 2781 | ||||
-rw-r--r-- | winsup/cygwin/tty.cc | 8 |
10 files changed, 2929 insertions, 116 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index f1e9471b538..ec0bc21287f 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,59 @@ +2003-03-08 Christopher Faylor <cgf@redhat.com> + + * syscalls.cc (unlink): Always attempt to use FILE_FLAG_CLOSE_ON_DELETE + to delete files since this allows us to preserve the protection of hard + linked files. + (link): Generate full path for potentially recalculated .lnk target. + +2003-03-08 Christopher Faylor <cgf@redhat.com> + + Revert below changes regarding _pinfo::cmdline. + +2003-03-08 Corinna Vinschen <corinna@vinschen.de> + + * external.cc (cygwin_internal): Change n to __off64_t to match change + of _pinfo::cmdline. + * fhandler.h (class fhandler_virtual): Change filesize member to + __off64_t. + * fhandler_proc.cc (format_proc_meminfo): Change to return __off64_t. + (format_proc_stat): Ditto. + (format_proc_uptime): Ditto. + * fhandler_process.cc (format_process_stat): Ditto. + (format_process_status): Ditto. + (format_process_statm): Ditto. + * pinfo.cc (_pinfo::cmdline): Expect __off64_t parameter. + * pinfo.h (class _pinfo): Change declaration of cmdline accordingly. + +2003-03-07 Christopher Faylor <cgf@redhat.com> + + * path.h (PATH_LNK): New enum val. + (path_conv::is_lnk_symlink): New function. True if path represents + .lnk style symlink. + * path.cc (check_shortcut): Set PATH_LNK in pflags when appropriate. + (symlink_info::check): Ditto. Remove PATH_LNK from pflags initially. + * syscalls.cc (unlink): Always remove readonly attribute from a symlink + regardless of type. + (link): (from Corinna Vinschen) Allow links to symlinks. Reset + attributes on a symlink after successful link creation. + (chmod): Use is_lnk_symlink where appropriate. + (rename): Ditto. + +2003-03-07 Pierre Humblet <pierre.humblet@ieee.org> + + * tty.cc (create_tty_master): Call GetComputerName instead of + cygwin_gethostname. Set ut_id. + * syscalls.cc (login): Call endutent. + (setutent): Do not seek after a fresh open. + +2003-03-07 Corinna Vinschen <corinna@vinschen.de> + + * syscalls.cc (seteuid32): Fix formatting. + +2003-03-04 Thomas Pfaff <tpfaff@gmx.net> + + * thread.cc (MTinterface::fixup_after_fork): Initialize mainthread + prior to pthread objects. + 2003-03-04 Jason Tishler <jason@tishler.net> * fhandler_socket.cc (fhandler_socket::dup): Initialize type. diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 918043f08e1..9bd337bd06f 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1094,7 +1094,8 @@ class fhandler_virtual : public fhandler_base { protected: char *filebuf; - size_t bufalloc, filesize; + size_t bufalloc; + __off64_t filesize; __off64_t position; int fileid; // unique within each class public: diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc index c0888e86e16..a5fbab53b56 100644 --- a/winsup/cygwin/fhandler_proc.cc +++ b/winsup/cygwin/fhandler_proc.cc @@ -70,9 +70,9 @@ static const DWORD proc_fhandlers[PROC_LINK_COUNT] = { const char proc[] = "/proc"; const int proc_len = sizeof (proc) - 1; -static off_t format_proc_meminfo (char *destbuf, size_t maxsize); -static off_t format_proc_stat (char *destbuf, size_t maxsize); -static off_t format_proc_uptime (char *destbuf, size_t maxsize); +static __off64_t format_proc_meminfo (char *destbuf, size_t maxsize); +static __off64_t format_proc_stat (char *destbuf, size_t maxsize); +static __off64_t format_proc_uptime (char *destbuf, size_t maxsize); /* auxillary function that returns the fhandler associated with the given path * this is where it would be nice to have pattern matching in C - polymorphism @@ -361,8 +361,7 @@ fhandler_proc::fill_filebuf () return true; } -static -off_t +static __off64_t format_proc_meminfo (char *destbuf, size_t maxsize) { unsigned long mem_total = 0UL, mem_free = 0UL, swap_total = 0UL, @@ -392,8 +391,7 @@ format_proc_meminfo (char *destbuf, size_t maxsize) swap_total >> 10, swap_free >> 10); } -static -off_t +static __off64_t format_proc_uptime (char *destbuf, size_t maxsize) { unsigned long long uptime = 0ULL, idle_time = 0ULL; @@ -423,8 +421,7 @@ format_proc_uptime (char *destbuf, size_t maxsize) idle_time / 100, long (idle_time % 100)); } -static -off_t +static __off64_t format_proc_stat (char *destbuf, size_t maxsize) { unsigned long long user_time = 0ULL, kernel_time = 0ULL, idle_time = 0ULL; diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc index 2f339654221..0e124a8a22e 100644 --- a/winsup/cygwin/fhandler_process.cc +++ b/winsup/cygwin/fhandler_process.cc @@ -67,9 +67,9 @@ static const char * const process_listing[] = static const int PROCESS_LINK_COUNT = (sizeof (process_listing) / sizeof (const char *)) - 1; -static off_t format_process_stat (_pinfo *p, char *destbuf, size_t maxsize); -static off_t format_process_status (_pinfo *p, char *destbuf, size_t maxsize); -static off_t format_process_statm (_pinfo *p, char *destbuf, size_t maxsize); +static __off64_t format_process_stat (_pinfo *p, char *destbuf, size_t maxsize); +static __off64_t format_process_status (_pinfo *p, char *destbuf, size_t maxsize); +static __off64_t format_process_statm (_pinfo *p, char *destbuf, size_t maxsize); static int get_process_state (DWORD dwProcessId); static bool get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss, unsigned long *vmtext, @@ -300,7 +300,9 @@ fhandler_process::fill_filebuf () { if (filebuf) free (filebuf); - filebuf = p->cmdline (filesize); + size_t fs; + filebuf = p->cmdline (fs); + filesize = fs; if (!filebuf || !*filebuf) filebuf = strdup ("<defunct>"); break; @@ -363,8 +365,7 @@ fhandler_process::fill_filebuf () return true; } -static -off_t +static __off64_t format_process_stat (_pinfo *p, char *destbuf, size_t maxsize) { char cmd[MAX_PATH]; @@ -499,8 +500,7 @@ format_process_stat (_pinfo *p, char *destbuf, size_t maxsize) ); } -static -off_t +static __off64_t format_process_status (_pinfo *p, char *destbuf, size_t maxsize) { char cmd[MAX_PATH]; @@ -593,8 +593,7 @@ format_process_status (_pinfo *p, char *destbuf, size_t maxsize) ); } -static -off_t +static __off64_t format_process_statm (_pinfo *p, char *destbuf, size_t maxsize) { unsigned long vmsize = 0UL, vmrss = 0UL, vmtext = 0UL, vmdata = 0UL, @@ -610,8 +609,7 @@ format_process_statm (_pinfo *p, char *destbuf, size_t maxsize) ); } -static -int +static int get_process_state (DWORD dwProcessId) { /* @@ -678,8 +676,7 @@ out: return state; } -static -bool +static bool get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss, unsigned long *vmtext, unsigned long *vmdata, unsigned long *vmlib, unsigned long *vmshare) diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index cad759aeb55..18609976804 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -2564,7 +2564,7 @@ check_shortcut (const char *path, DWORD fileattr, HANDLE h, contents[len] = '\0'; res = len; if (res) /* It's a symlink. */ - *pflags = PATH_SYMLINK; + *pflags = PATH_SYMLINK | PATH_LNK; goto close_it; file_not_symlink: @@ -2821,13 +2821,13 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) suffix_scan suffix; contents[0] = '\0'; - is_symlink = TRUE; + is_symlink = true; ext_here = suffix.has (path, suffixes); extn = ext_here - path; major = 0; minor = 0; - pflags &= ~PATH_SYMLINK; + pflags &= ~(PATH_SYMLINK | PATH_LNK); case_clash = false; @@ -2876,6 +2876,8 @@ symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt) (res = get_symlink_ea (suffix.path, contents, sizeof (contents))) > 0) { pflags = PATH_SYMLINK; + if (sym_check == 1) + pflags |= PATH_LNK; debug_printf ("Got symlink from EA: %s", contents); break; } diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index 9836d9ebce8..9cc3fcf49b0 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -60,6 +60,7 @@ enum path_types PATH_NOTEXEC = MOUNT_NOTEXEC, PATH_CYGWIN_EXEC = MOUNT_CYGWIN_EXEC, PATH_ALL_EXEC = (PATH_CYGWIN_EXEC | PATH_EXEC), + PATH_LNK = 0x01000000, PATH_TEXT = 0x02000000, PATH_ISDISK = 0x04000000, PATH_HAS_SYMLINKS = 0x10000000, @@ -109,6 +110,7 @@ class path_conv return 0; } int issymlink () const {return path_flags & PATH_SYMLINK;} + int is_lnk_symlink () const {return path_flags & PATH_LNK;} int isdevice () const {return dev.devn && dev.devn != FH_FS && dev.devn != FH_FIFO;} int isfifo () const {return dev == FH_FIFO;} int isspecial () const {return dev.devn && dev.devn != FH_FS;} diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc index b3fdbb51785..bd07e48a65e 100644 --- a/winsup/cygwin/pinfo.cc +++ b/winsup/cygwin/pinfo.cc @@ -520,7 +520,8 @@ pinfo::release () if (h) { #ifdef DEBUGGING - if (((DWORD) procinfo & 0x77000000) == 0x61000000) try_to_debug (); + if (((DWORD) procinfo & 0x77000000) == 0x61000000) + try_to_debug (); #endif UnmapViewOfFile (procinfo); procinfo = NULL; diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index ba2d9580ed0..3ccfabcdb4c 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -139,31 +139,31 @@ unlink (const char *ourname) goto done; } - /* Check for shortcut as symlink condition. */ - if (win32_name.has_attribute (FILE_ATTRIBUTE_READONLY)) - { - int len = strlen (win32_name); - if (len > 4 && strcasematch ((char *) win32_name + len - 4, ".lnk")) - SetFileAttributes (win32_name, (DWORD) win32_name & ~FILE_ATTRIBUTE_READONLY); - } + SetFileAttributes (win32_name, (DWORD) win32_name & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)); + /* Attempt to use "delete on close" semantics to handle removing + a file which may be open. */ + HANDLE h; + h = CreateFile (win32_name, GENERIC_READ, FILE_SHARE_DELETE, &sec_none_nih, + OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); + (void) SetFileAttributes (win32_name, (DWORD) win32_name); + (void) DeleteFile (win32_name); DWORD lasterr; - lasterr = 0; - for (int i = 0; i < 2; i++) - { - if (DeleteFile (win32_name)) - { - syscall_printf ("DeleteFile succeeded"); - goto ok; - } + lasterr = GetLastError (); + if (h != INVALID_HANDLE_VALUE) + CloseHandle (h); - lasterr = GetLastError (); - if (i || lasterr != ERROR_ACCESS_DENIED || win32_name.issymlink ()) - break; /* Couldn't delete it. */ + if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES + || (!win32_name.isremote () && wincap.has_delete_on_close ())) + { + syscall_printf ("DeleteFile succeeded"); + goto ok; + } - /* if access denied, chmod to be writable, in case it is not, - and try again */ - (void) chmod (win32_name, 0777); + if (DeleteFile (win32_name)) + { + syscall_printf ("DeleteFile after CreateFile/ClosHandle succeeded"); + goto ok; } /* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing @@ -173,45 +173,6 @@ unlink (const char *ourname) && !win32_name.isremote ()) lasterr = ERROR_SHARING_VIOLATION; - /* Tried to delete file by normal DeleteFile and by resetting protection - and then deleting. That didn't work. - - There are two possible reasons for this: 1) The file may be opened and - Windows is not allowing it to be deleted, or 2) We may not have permissions - to delete the file. - - So, first assume that it may be 1) and try to remove the file using the - Windows FILE_FLAG_DELETE_ON_CLOSE semantics. This seems to work only - spottily on Windows 9x/Me but it does seem to work reliably on NT as - long as the file doesn't exist on a remote drive. */ - - bool delete_on_close_ok; - - delete_on_close_ok = !win32_name.isremote () - && wincap.has_delete_on_close (); - - /* Attempt to use "delete on close" semantics to handle removing - a file which may be open. */ - HANDLE h; - h = CreateFile (win32_name, GENERIC_READ, FILE_SHARE_READ, &sec_none_nih, - OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); - if (h == INVALID_HANDLE_VALUE) - { - if (GetLastError () == ERROR_FILE_NOT_FOUND) - goto ok; - } - else - { - CloseHandle (h); - syscall_printf ("CreateFile/CloseHandle succeeded"); - /* Everything is fine if the file has disappeared or if we know that the - FILE_FLAG_DELETE_ON_CLOSE will eventually work. */ - if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES - || delete_on_close_ok) - goto ok; /* The file is either gone already or will eventually be - deleted by the OS. */ - } - /* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing violation, then queue the file for deletion when the process exits. Otherwise, punt. */ @@ -629,8 +590,9 @@ link (const char *a, const char *b) { int res = -1; sigframe thisframe (mainthread); - path_conv real_a (a, PC_SYM_FOLLOW | PC_FULL); + path_conv real_a (a, PC_SYM_NOFOLLOW | PC_FULL); path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL); + extern BOOL allow_winsymlinks; if (real_a.error) { @@ -658,14 +620,20 @@ link (const char *a, const char *b) goto done; } + /* Shortcut hack. */ + char new_lnk_buf[MAX_PATH + 5]; + if (allow_winsymlinks && real_a.is_lnk_symlink () && !real_b.case_clash) + { + strcpy (new_lnk_buf, b); + strcat (new_lnk_buf, ".lnk"); + b = new_lnk_buf; + real_b.check (b, PC_SYM_NOFOLLOW | PC_FULL); + } /* Try to make hard link first on Windows NT */ if (wincap.has_hard_links ()) { if (CreateHardLinkA (real_b, real_a, NULL)) - { - res = 0; - goto done; - } + goto success; HANDLE hFileSource; @@ -747,7 +715,13 @@ link (const char *a, const char *b) if (!bSuccess) goto docopy; + success: res = 0; + if (!allow_winsymlinks && real_a.is_lnk_symlink ()) + SetFileAttributes (real_b, (DWORD) real_a + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_READONLY); + goto done; } docopy: @@ -947,7 +921,7 @@ chmod (const char *path, mode_t mode) else (DWORD) win32_path |= FILE_ATTRIBUTE_READONLY; - if (S_ISLNK (mode) || S_ISSOCK (mode)) + if (!win32_path.is_lnk_symlink () && S_ISLNK (mode) || S_ISSOCK (mode)) (DWORD) win32_path |= FILE_ATTRIBUTE_SYSTEM; if (!SetFileAttributes (win32_path, win32_path)) @@ -1280,16 +1254,12 @@ rename (const char *oldpath, const char *newpath) /* Shortcut hack. */ char new_lnk_buf[MAX_PATH + 5]; - if (real_old.issymlink () && !real_new.error && !real_new.case_clash) + if (real_old.is_lnk_symlink () && !real_new.error && !real_new.case_clash) { - int len_old = strlen (real_old.get_win32 ()); - if (strcasematch (real_old.get_win32 () + len_old - 4, ".lnk")) - { - strcpy (new_lnk_buf, newpath); - strcat (new_lnk_buf, ".lnk"); - newpath = new_lnk_buf; - real_new.check (newpath, PC_SYM_NOFOLLOW); - } + strcpy (new_lnk_buf, newpath); + strcat (new_lnk_buf, ".lnk"); + newpath = new_lnk_buf; + real_new.check (newpath, PC_SYM_NOFOLLOW); } if (real_new.error || real_new.case_clash) @@ -1319,9 +1289,8 @@ rename (const char *oldpath, const char *newpath) SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY); /* Shortcut hack No. 2, part 1 */ - if (!real_old.issymlink () && !real_new.error && real_new.issymlink () && - real_new.known_suffix && strcasematch (real_new.known_suffix, ".lnk") && - (lnk_suffix = strrchr (real_new.get_win32 (), '.'))) + if (!real_old.issymlink () && !real_new.error && real_new.is_lnk_symlink () + && (lnk_suffix = strrchr (real_new.get_win32 (), '.'))) *lnk_suffix = '\0'; if (!MoveFile (real_old, real_new)) @@ -2106,7 +2075,8 @@ seteuid32 (__uid32_t uid) /* Set process def dacl to allow access to impersonated token */ char dacl_buf[MAX_DACL_LEN (5)]; - if (usersid != (origpsid = cygheap->user.orig_sid ())) psid2 = usersid; + if (usersid != (origpsid = cygheap->user.orig_sid ())) + psid2 = usersid; if (sec_acl ((PACL) dacl_buf, FALSE, origpsid, psid2)) { TOKEN_DEFAULT_DACL tdacl; @@ -2176,11 +2146,11 @@ success: groups.ischanged = FALSE; return 0; - failed: +failed: cygheap->user.token = sav_token; cygheap->user.impersonated = sav_impersonated; if (cygheap->user.issetuid () - && !ImpersonateLoggedOnUser (cygheap->user.token)) + && !ImpersonateLoggedOnUser (cygheap->user.token)) system_printf ("Impersonating in seteuid failed: %E"); return -1; } @@ -2537,6 +2507,7 @@ login (struct utmp *ut) register int fd; pututline (ut); + endutent (); if ((fd = open (_PATH_WTMP, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0) { (void) write (fd, (char *) ut, sizeof (struct utmp)); @@ -2584,8 +2555,8 @@ logout (char *line) /* Found the entry for LINE; mark it as logged out. */ { /* Zero out entries describing who's logged in. */ - bzero (ut->ut_name, sizeof (ut->ut_name)); - bzero (ut->ut_host, sizeof (ut->ut_host)); + memset (ut->ut_name, 0, sizeof (ut->ut_name)); + memset (ut->ut_host, 0, sizeof (ut->ut_host)); time (&ut->ut_time); /* Now seek back to the position in utmp at which UT occured, @@ -2616,10 +2587,9 @@ setutent () { sigframe thisframe (mainthread); if (utmp_fd == -2) - { - utmp_fd = open (utmp_file, O_RDWR); - } - lseek (utmp_fd, 0, SEEK_SET); + utmp_fd = open (utmp_file, O_RDWR); + else + lseek (utmp_fd, 0, SEEK_SET); } extern "C" void diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc new file mode 100644 index 00000000000..9f8839fb12c --- /dev/null +++ b/winsup/cygwin/thread.cc @@ -0,0 +1,2781 @@ +/* thread.cc: Locking and threading module functions + + Copyright 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc. + + Originally written by Marco Fuykschot <marco@ddi.nl> + Substantialy enhanced by Robert Collins <rbtcollins@hotmail.com> + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +/* Implementation overview and caveats: + + Win32 puts some contraints on what can and cannot be implemented. Where + possible we work around those contrainsts. Where we cannot work around + the constraints we either pretend to be conformant, or return an error + code. + + Some caveats: PROCESS_SHARED objects while they pretend to be process + shared, may not actually work. Some test cases are needed to determine + win32's behaviour. My suspicion is that the win32 handle needs to be + opened with different flags for proper operation. + + R.Collins, April 2001. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef _MT_SAFE +#include "winsup.h" +#include <limits.h> +#include <errno.h> +#include "cygerrno.h" +#include <assert.h> +#include <stdlib.h> +#include <syslog.h> +#include "pinfo.h" +#include "perprocess.h" +#include "security.h" +#include <semaphore.h> +#include <stdio.h> +#include <sys/timeb.h> + +extern int threadsafe; + +struct _reent * +_reent_clib () +{ + struct __reent_t *_r = + (struct __reent_t *) MT_INTERFACE->reent_key.get (); + +#ifdef _CYG_THREAD_FAILSAFE + if (_r == 0) + system_printf ("local thread storage not inited"); +#endif + return _r->_clib; +} + +struct _winsup_t * +_reent_winsup () +{ + struct __reent_t *_r = + (struct __reent_t *) MT_INTERFACE->reent_key.get (); + +#ifdef _CYG_THREAD_FAILSAFE + if (_r == 0) + system_printf ("local thread storage not inited"); +#endif + return _r->_winsup; +} + +bool +nativeMutex::init () +{ + theHandle = CreateMutex (&sec_none_nih, FALSE, NULL); + if (!theHandle) + { + debug_printf ("CreateMutex failed. %E"); + return false; + } + return true; +} + +bool +nativeMutex::lock () +{ + DWORD waitResult = WaitForSingleObject (theHandle, INFINITE); + if (waitResult != WAIT_OBJECT_0) + { + system_printf ("Received unexpected wait result %d on handle %p, %E", waitResult, theHandle); + return false; + } + return true; +} + +void +nativeMutex::unlock () +{ + if (!ReleaseMutex (theHandle)) + system_printf ("Received a unexpected result releasing mutex. %E"); +} + +inline LPCRITICAL_SECTION +ResourceLocks::Lock (int _resid) +{ +#ifdef _CYG_THREAD_FAILSAFE + if (!inited) + system_printf ("lock called before initialization"); + + thread_printf + ("Get Resource lock %d ==> %p for %p , real : %d , threadid %d ", _resid, + &lock, user_data, myself->pid, GetCurrentThreadId ()); +#endif + return &lock; +} + +void +SetResourceLock (int _res_id, int _mode, const char *_function) +{ +#ifdef _CYG_THREAD_FAILSAFE + thread_printf ("Set resource lock %d mode %d for %s start", + _res_id, _mode, _function); +#endif + EnterCriticalSection (user_data->resourcelocks->Lock (_res_id)); + +#ifdef _CYG_THREAD_FAILSAFE + user_data->resourcelocks->owner = GetCurrentThreadId (); + user_data->resourcelocks->count++; +#endif +} + +void +ReleaseResourceLock (int _res_id, int _mode, const char *_function) +{ +#ifdef _CYG_THREAD_FAILSAFE + thread_printf ("Release resource lock %d mode %d for %s done", _res_id, + _mode, _function); + + AssertResourceOwner (_res_id, _mode); + user_data->resourcelocks->count--; + if (user_data->resourcelocks->count == 0) + user_data->resourcelocks->owner = 0; +#endif + + LeaveCriticalSection (user_data->resourcelocks->Lock (_res_id)); +} + +#ifdef _CYG_THREAD_FAILSAFE +void +AssertResourceOwner (int _res_id, int _mode) +{ + + thread_printf + ("Assert Resource lock %d ==> for %p , real : %d , threadid %d count %d owner %d", + _res_id, user_data, myself->pid, GetCurrentThreadId (), + user_data->resourcelocks->count, user_data->resourcelocks->owner); + if (user_data && (user_data->resourcelocks->owner != GetCurrentThreadId ())) + system_printf ("assertion failed, not the resource owner"); +} + +#endif + +void +ResourceLocks::Init () +{ + InitializeCriticalSection (&lock); + inited = true; + +#ifdef _CYG_THREAD_FAILSAFE + owner = 0; + count = 0; +#endif + + thread_printf ("lock %p inited by %p , %d", &lock, user_data, myself->pid); +} + +void +ResourceLocks::Delete () +{ + if (inited) + { + thread_printf ("Close Resource Locks %p ", &lock); + DeleteCriticalSection (&lock); + inited = false; + } +} + +void +MTinterface::Init (int forked) +{ + reents._clib = _impure_ptr; + reents._winsup = &winsup_reent; + winsup_reent._process_logmask = LOG_UPTO (LOG_DEBUG); + + if (!forked) + reent_key.set (&reents); + + pthread_mutex::initMutex (); + pthread_cond::initMutex (); +} + +void +MTinterface::fixup_before_fork (void) +{ + pthread_key::fixup_before_fork (); +} + +/* This function is called from a single threaded process */ +void +MTinterface::fixup_after_fork (void) +{ + pthread_key::fixup_after_fork (); + + threadcount = 1; + pthread::initMainThread (true); + + pthread_mutex *mutex = mutexs; + debug_printf ("mutexs is %x",mutexs); + while (mutex) + { + mutex->fixup_after_fork (); + mutex = mutex->next; + } + pthread_cond *cond = conds; + debug_printf ("conds is %x",conds); + while (cond) + { + cond->fixup_after_fork (); + cond = cond->next; + } + semaphore *sem = semaphores; + debug_printf ("semaphores is %x",semaphores); + while (sem) + { + sem->fixup_after_fork (); + sem = sem->next; + } +} + +/* pthread calls */ + +/* static methods */ +void +pthread::initMainThread (bool do_init) +{ + if (!do_init) + return; + + pthread *thread = getTlsSelfPointer (); + if (!thread) + { + thread = new pthread (); + if (!thread) + api_fatal ("failed to create mainthread object"); + } + + thread->initCurrentThread (); +} + +pthread * +pthread::self () +{ + pthread *thread = getTlsSelfPointer (); + if (thread) + return thread; + return pthreadNull::getNullpthread (); +} + +void +pthread::setTlsSelfPointer (pthread *thisThread) +{ + MT_INTERFACE->thread_self_key.set (thisThread); +} + +pthread * +pthread::getTlsSelfPointer () +{ + return (pthread *) MT_INTERFACE->thread_self_key.get (); +} + + + +/* member methods */ +pthread::pthread ():verifyable_object (PTHREAD_MAGIC), win32_obj_id (0), + cancelstate (0), canceltype (0), cancel_event (0), + joiner (NULL), cleanup_stack (NULL) +{ +} + +pthread::~pthread () +{ + if (win32_obj_id) + CloseHandle (win32_obj_id); + if (cancel_event) + CloseHandle (cancel_event); +} + +void +pthread::setThreadIdtoCurrent () +{ + thread_id = GetCurrentThreadId (); +} + +void +pthread::precreate (pthread_attr *newattr) +{ + pthread_mutex *verifyable_mutex_obj = &mutex; + + /* already running ? */ + if (win32_obj_id) + return; + + if (newattr) + { + attr.joinable = newattr->joinable; + attr.contentionscope = newattr->contentionscope; + attr.inheritsched = newattr->inheritsched; + attr.stacksize = newattr->stacksize; + } + + if (!pthread_mutex::isGoodObject (&verifyable_mutex_obj)) + { + thread_printf ("New thread object access mutex is not valid. this %p", + this); + magic = 0; + return; + } + + cancel_event = ::CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + if (!cancel_event) + { + system_printf ("couldn't create cancel event, this %p LastError %E", this); + /* we need the event for correct behaviour */ + magic = 0; + return; + } +} + +void +pthread::create (void *(*func) (void *), pthread_attr *newattr, + void *threadarg) +{ + precreate (newattr); + if (!magic) + return; + function = func; + arg = threadarg; + + win32_obj_id = ::CreateThread (&sec_none_nih, attr.stacksize, + (LPTHREAD_START_ROUTINE) thread_init_wrapper, + this, CREATE_SUSPENDED, &thread_id); + + if (!win32_obj_id) + { + thread_printf ("CreateThread failed: this %p LastError %E", this); + magic = 0; + } + else { + postcreate (); + ResumeThread (win32_obj_id); + } +} + +void +pthread::postcreate () +{ + InterlockedIncrement (&MT_INTERFACE->threadcount); + /* FIXME: set the priority appropriately for system contention scope */ + if (attr.inheritsched == PTHREAD_EXPLICIT_SCHED) + { + /* FIXME: set the scheduling settings for the new thread */ + /* sched_thread_setparam (win32_obj_id, attr.schedparam); */ + } +} + +void +pthread::exit (void *value_ptr) +{ + class pthread *thread = this; + + // run cleanup handlers + pop_all_cleanup_handlers (); + + pthread_key::runAllDestructors (); + + mutex.Lock (); + // cleanup if thread is in detached state and not joined + if (__pthread_equal (&joiner, &thread)) + delete this; + else + { + return_ptr = value_ptr; + mutex.UnLock (); + } + + if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0) + ::exit (0); + else + ExitThread (0); +} + +int +pthread::cancel (void) +{ + class pthread *thread = this; + class pthread *self = pthread::self (); + + mutex.Lock (); + + if (canceltype == PTHREAD_CANCEL_DEFERRED || + cancelstate == PTHREAD_CANCEL_DISABLE) + { + // cancel deferred + mutex.UnLock (); + SetEvent (cancel_event); + return 0; + } + + else if (__pthread_equal (&thread, &self)) + { + mutex.UnLock (); + cancel_self (); + return 0; // Never reached + } + + // cancel asynchronous + SuspendThread (win32_obj_id); + if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT) + { + CONTEXT context; + context.ContextFlags = CONTEXT_CONTROL; + GetThreadContext (win32_obj_id, &context); + context.Eip = (DWORD) pthread::static_cancel_self; + SetThreadContext (win32_obj_id, &context); + } + mutex.UnLock (); + ResumeThread (win32_obj_id); + + return 0; +/* + TODO: insert pthread_testcancel into the required functions + the required function list is: *indicates done, X indicates not present in cygwin. +aio_suspend () +*close () +*creat () +fcntl () +fsync () +getmsg () +getpmsg () +lockf () +mq_receive () +mq_send () +msgrcv () +msgsnd () +msync () +nanosleep () +open () +*pause () +poll () +pread () +pthread_cond_timedwait () +pthread_cond_wait () +*pthread_join () +*pthread_testcancel () +putmsg () +putpmsg () +pwrite () +read () +readv () +select () +*sem_wait () +*sigpause () +*sigsuspend () +sigtimedwait () +sigwait () +sigwaitinfo () +*sleep () +*system () +tcdrain () +*usleep () +*wait () +*wait3() +waitid () +*waitpid () +write () +writev () + +the optional list is: +catclose () +catgets () +catopen () +closedir () +closelog () +ctermid () +dbm_close () +dbm_delete () +dbm_fetch () +dbm_nextkey () +dbm_open () +dbm_store () +dlclose () +dlopen () +endgrent () +endpwent () +endutxent () +fclose () +fcntl () +fflush () +fgetc () +fgetpos () +fgets () +fgetwc () +fgetws () +fopen () +fprintf () +fputc () +fputs () +fputwc () +fputws () +fread () +freopen () +fscanf () +fseek () +fseeko () +fsetpos () +ftell () +ftello () +ftw () +fwprintf () +fwrite () +fwscanf () +getc () +getc_unlocked () +getchar () +getchar_unlocked () +getcwd () +getdate () +getgrent () +getgrgid () +getgrgid_r () +getgrnam () +getgrnam_r () +getlogin () +getlogin_r () +getpwent () +*getpwnam () +*getpwnam_r () +*getpwuid () +*getpwuid_r () +gets () +getutxent () +getutxid () +getutxline () +getw () +getwc () +getwchar () +getwd () +glob () +iconv_close () +iconv_open () +ioctl () +lseek () +mkstemp () +nftw () +opendir () +openlog () +pclose () +perror () +popen () +printf () +putc () +putc_unlocked () +putchar () +putchar_unlocked () +puts () +pututxline () +putw () +putwc () +putwchar () +readdir () +readdir_r () +remove () +rename () +rewind () +rewinddir () +scanf () +seekdir () +semop () +setgrent () +setpwent () +setutxent () +strerror () +syslog () +tmpfile () +tmpnam () +ttyname () +ttyname_r () +ungetc () +ungetwc () +unlink () +vfprintf () +vfwprintf () +vprintf () +vwprintf () +wprintf () +wscanf () + +Note, that for fcntl (), for any value of the cmd argument. + +And we must not introduce cancellation points anywhere else that's part of the posix or +opengroup specs. + */ +} + +void +pthread::testcancel (void) +{ + if (cancelstate == PTHREAD_CANCEL_DISABLE) + return; + + if (WAIT_OBJECT_0 == WaitForSingleObject (cancel_event, 0)) + cancel_self (); +} + +void +pthread::static_cancel_self (void) +{ + pthread::self ()->cancel_self (); +} + + +DWORD +pthread::cancelable_wait (HANDLE object, DWORD timeout, const bool do_cancel) +{ + DWORD res; + HANDLE wait_objects[2]; + pthread_t thread = self (); + + if (!isGoodObject (&thread) || thread->cancelstate == PTHREAD_CANCEL_DISABLE) + return WaitForSingleObject (object, timeout); + + // Do not change the wait order + // The object must have higher priority than the cancel event, + // because WaitForMultipleObjects will return the smallest index + // if both objects are signaled + wait_objects[0] = object; + wait_objects[1] = thread->cancel_event; + + res = WaitForMultipleObjects (2, wait_objects, FALSE, timeout); + if (do_cancel && res == WAIT_CANCELED) + pthread::static_cancel_self (); + return res; +} + +int +pthread::setcancelstate (int state, int *oldstate) +{ + int result = 0; + + mutex.Lock (); + + if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) + result = EINVAL; + else + { + if (oldstate) + *oldstate = cancelstate; + cancelstate = state; + } + + mutex.UnLock (); + + return result; +} + +int +pthread::setcanceltype (int type, int *oldtype) +{ + int result = 0; + + mutex.Lock (); + + if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS) + result = EINVAL; + else + { + if (oldtype) + *oldtype = canceltype; + canceltype = type; + } + + mutex.UnLock (); + + return result; +} + +void +pthread::push_cleanup_handler (__pthread_cleanup_handler *handler) +{ + if (this != self ()) + // TODO: do it? + api_fatal ("Attempt to push a cleanup handler across threads"); + handler->next = cleanup_stack; + InterlockedExchangePointer (&cleanup_stack, handler); +} + +void +pthread::pop_cleanup_handler (int const execute) +{ + if (this != self ()) + // TODO: send a signal or something to the thread ? + api_fatal ("Attempt to execute a cleanup handler across threads"); + + mutex.Lock (); + + if (cleanup_stack != NULL) + { + __pthread_cleanup_handler *handler = cleanup_stack; + + if (execute) + (*handler->function) (handler->arg); + cleanup_stack = handler->next; + } + + mutex.UnLock (); +} + +void +pthread::pop_all_cleanup_handlers () +{ + while (cleanup_stack != NULL) + pop_cleanup_handler (1); +} + +void +pthread::cancel_self () +{ + exit (PTHREAD_CANCELED); +} + +DWORD +pthread::getThreadId () +{ + return thread_id; +} + +void +pthread::initCurrentThread () +{ + cancel_event = ::CreateEvent (&sec_none_nih, TRUE, FALSE, NULL); + if (!DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), &win32_obj_id, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + win32_obj_id = NULL; + setThreadIdtoCurrent (); + setTlsSelfPointer (this); +} + +/* static members */ +bool +pthread_attr::isGoodObject (pthread_attr_t const *attr) +{ + if (verifyable_object_isvalid (attr, PTHREAD_ATTR_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +/* instance members */ + +pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC), +joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS), +inheritsched (PTHREAD_INHERIT_SCHED), stacksize (0) +{ + schedparam.sched_priority = 0; +} + +pthread_attr::~pthread_attr () +{ +} + +bool +pthread_condattr::isGoodObject (pthread_condattr_t const *attr) +{ + if (verifyable_object_isvalid (attr, PTHREAD_CONDATTR_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +pthread_condattr::pthread_condattr ():verifyable_object + (PTHREAD_CONDATTR_MAGIC), shared (PTHREAD_PROCESS_PRIVATE) +{ +} + +pthread_condattr::~pthread_condattr () +{ +} + +/* This is used for cond creation protection within a single process only */ +nativeMutex NO_COPY pthread_cond::condInitializationLock; + +/* We can only be called once. + TODO: (no rush) use a non copied memory section to + hold an initialization flag. */ +void +pthread_cond::initMutex () +{ + if (!condInitializationLock.init ()) + api_fatal ("Could not create win32 Mutex for pthread cond static initializer support."); +} + +pthread_cond::pthread_cond (pthread_condattr *attr):verifyable_object (PTHREAD_COND_MAGIC) +{ + int temperr; + this->shared = attr ? attr->shared : PTHREAD_PROCESS_PRIVATE; + this->mutex = NULL; + this->waiting = 0; + + this->win32_obj_id = ::CreateEvent (&sec_none_nih, false, /* auto signal reset - which I think is pthreads like ? */ + false, /* start non signaled */ + NULL /* no name */); + /* TODO: make a shared mem mutex if out attributes request shared mem cond */ + cond_access = NULL; + if ((temperr = pthread_mutex_init (&this->cond_access, NULL))) + { + system_printf ("couldn't init mutex, this %p errno %d", this, temperr); + /* we need the mutex for correct behaviour */ + magic = 0; + } + + if (!this->win32_obj_id) + magic = 0; + /* threadsafe addition is easy */ + next = (pthread_cond *) InterlockedExchangePointer (&MT_INTERFACE->conds, this); +} + +pthread_cond::~pthread_cond () +{ + if (win32_obj_id) + CloseHandle (win32_obj_id); + pthread_mutex_destroy (&cond_access); + /* I'm not 100% sure the next bit is threadsafe. I think it is... */ + if (MT_INTERFACE->conds == this) + InterlockedExchangePointer (&MT_INTERFACE->conds, this->next); + else + { + pthread_cond *tempcond = MT_INTERFACE->conds; + while (tempcond->next && tempcond->next != this) + tempcond = tempcond->next; + /* but there may be a race between the loop above and this statement */ + InterlockedExchangePointer (&tempcond->next, this->next); + } +} + +void +pthread_cond::BroadCast () +{ + /* TODO: implement the same race fix as Signal has */ + if (pthread_mutex_lock (&cond_access)) + system_printf ("Failed to lock condition variable access mutex, this %p", this); + int count = waiting; + if (!pthread_mutex::isGoodObject (&mutex)) + { + if (pthread_mutex_unlock (&cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", this); + /* This isn't and API error - users are allowed to call this when no threads + are waiting + system_printf ("Broadcast called with invalid mutex"); + */ + return; + } + while (count--) + PulseEvent (win32_obj_id); + if (pthread_mutex_unlock (&cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", this); +} + +void +pthread_cond::Signal () +{ + if (pthread_mutex_lock (&cond_access)) + system_printf ("Failed to lock condition variable access mutex, this %p", this); + if (!pthread_mutex::isGoodObject (&mutex)) + { + if (pthread_mutex_unlock (&cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", + this); + return; + } + int temp = waiting; + if (!temp) + /* nothing to signal */ + { + if (pthread_mutex_unlock (&cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", this); + return; + } + /* Prime the detection flag */ + ExitingWait = 1; + /* Signal any waiting thread */ + PulseEvent (win32_obj_id); + /* No one can start waiting until we release the condition access mutex */ + /* The released thread will decrement waiting when it gets a time slice... + without waiting for the access mutex + * InterLockedIncrement on 98 +, NT4 + returns the incremented value. + * On 95, nt 3.51 < it returns a sign correct number - 0=0, + for greater than 0, - + * for less than 0. + * Because of this we cannot spin on the waiting count, but rather we need a + * dedicated flag for a thread exiting the Wait function. + * Also not that Interlocked* sync CPU caches with memory. + */ + int spins = 10; + /* When ExitingWait is nonzero after a decrement, the leaving thread has + * done it's thing + */ + while (InterlockedDecrement (&ExitingWait) == 0 && spins) + { + InterlockedIncrement (&ExitingWait); + /* give up the cpu to force a context switch. */ + low_priority_sleep (0); + if (spins == 5) + /* we've had 5 timeslices, and the woken thread still hasn't done it's + * thing - maybe we raced it with the event? */ + PulseEvent (win32_obj_id); + spins--; + } + if (waiting + 1 != temp) + system_printf ("Released too many threads - %d now %d originally", waiting, temp); + if (pthread_mutex_unlock (&cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", this); +} + +int +pthread_cond::TimedWait (DWORD dwMilliseconds) +{ + DWORD rv; + + // FIXME: race condition (potentially drop events + // Possible solution (single process only) - place this in a critical section. + mutex->UnLock (); + rv = WaitForSingleObject (win32_obj_id, dwMilliseconds); +#if 0 + /* we need to use native win32 mutex's here, because the cygwin ones now use + * critical sections, which are faster, but introduce a race _here_. Until then + * The NT variant of the code is redundant. + */ + + rv = SignalObjectAndWait (mutex->win32_obj_id, win32_obj_id, dwMilliseconds, + false); +#endif + switch (rv) + { + case WAIT_FAILED: + return 0; /* POSIX doesn't allow errors after we modify the mutex state */ + case WAIT_ABANDONED: + case WAIT_TIMEOUT: + return ETIMEDOUT; + case WAIT_OBJECT_0: + return 0; /* we have been signaled */ + default: + return 0; + } +} + +void +pthread_cond::fixup_after_fork () +{ + debug_printf ("cond %x in fixup_after_fork", this); + if (shared != PTHREAD_PROCESS_PRIVATE) + api_fatal ("doesn't understand PROCESS_SHARED condition variables"); + /* FIXME: duplicate code here and in the constructor. */ + this->win32_obj_id = ::CreateEvent (&sec_none_nih, false, false, NULL); + if (!win32_obj_id) + api_fatal ("failed to create new win32 mutex"); +#if DETECT_BAD_APPS + if (waiting) + api_fatal ("Forked () while a condition variable has waiting threads.\nReport to cygwin@cygwin.com"); +#else + waiting = 0; + mutex = NULL; +#endif +} + +/* pthread_key */ +/* static members */ +/* This stores pthread_key information across fork() boundaries */ +List<pthread_key> pthread_key::keys; + +void +pthread_key::saveAKey (pthread_key *key) +{ + key->saveKeyToBuffer (); +} + +void +pthread_key::fixup_before_fork () +{ + keys.forEach (saveAKey); +} + +void +pthread_key::restoreAKey (pthread_key *key) +{ + key->recreateKeyFromBuffer (); +} + +void +pthread_key::fixup_after_fork () +{ + keys.forEach (restoreAKey); +} + +void +pthread_key::destroyAKey (pthread_key *key) +{ + key->run_destructor (); +} + +void +pthread_key::runAllDestructors () +{ + keys.forEach (destroyAKey); +} + +bool +pthread_key::isGoodObject (pthread_key_t const *key) +{ + if (verifyable_object_isvalid (key, PTHREAD_KEY_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +/* non-static members */ + +pthread_key::pthread_key (void (*aDestructor) (void *)):verifyable_object (PTHREAD_KEY_MAGIC), destructor (aDestructor) +{ + dwTlsIndex = TlsAlloc (); + if (dwTlsIndex == TLS_OUT_OF_INDEXES) + magic = 0; + else + keys.Insert (this); +} + +pthread_key::~pthread_key () +{ + /* We may need to make the list code lock the list during operations + */ + if (magic != 0) + { + keys.Remove (this); + TlsFree (dwTlsIndex); + } +} + +int +pthread_key::set (const void *value) +{ + /* the OS function doesn't perform error checking */ + TlsSetValue (dwTlsIndex, (void *) value); + return 0; +} + +void * +pthread_key::get () const +{ + int savedError = ::GetLastError (); + void *result = TlsGetValue (dwTlsIndex); + ::SetLastError (savedError); + return result; +} + +void +pthread_key::saveKeyToBuffer () +{ + fork_buf = get (); +} + +void +pthread_key::recreateKeyFromBuffer () +{ + dwTlsIndex = TlsAlloc (); + if (dwTlsIndex == TLS_OUT_OF_INDEXES) + api_fatal ("pthread_key::recreateKeyFromBuffer () failed to reallocate Tls storage"); + set (fork_buf); +} + +void +pthread_key::run_destructor () +{ + if (destructor) + { + void *oldValue = get (); + if (oldValue) + { + set (NULL); + destructor (oldValue); + } + } +} + +/* pshared mutexs: + + REMOVED FROM CURRENT. These can be reinstated with the daemon, when all the + gymnastics can be a lot easier. + + the mutex_t (size 4) is not used as a verifyable object because we cannot + guarantee the same address space for all processes. + we use the following: + high bit set (never a valid address). + second byte is reserved for the priority. + third byte is reserved + fourth byte is the mutex id. (max 255 cygwin mutexs system wide). + creating mutex's does get slower and slower, but as creation is a one time + job, it should never become an issue + + And if you're looking at this and thinking, why not an array in cygwin for all mutexs, + - you incur a penalty on _every_ mutex call and you have toserialise them all. + ... Bad karma. + + option 2? put everything in userspace and update the ABI? + - bad karma as well - the HANDLE, while identical across process's, + Isn't duplicated, it's reopened. */ + +/* static members */ +bool +pthread_mutex::isGoodObject (pthread_mutex_t const *mutex) +{ + if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +bool +pthread_mutex::isGoodInitializer (pthread_mutex_t const *mutex) +{ + if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER) != VALID_STATIC_OBJECT) + return false; + return true; +} + +bool +pthread_mutex::isGoodInitializerOrObject (pthread_mutex_t const *mutex) +{ + if (verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER) == INVALID_OBJECT) + return false; + return true; +} + +bool +pthread_mutex::isGoodInitializerOrBadObject (pthread_mutex_t const *mutex) +{ + verifyable_object_state objectState = verifyable_object_isvalid (mutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER); + if (objectState == VALID_OBJECT) + return false; + return true; +} + +/* This is used for mutex creation protection within a single process only */ +nativeMutex NO_COPY pthread_mutex::mutexInitializationLock; + +/* We can only be called once. + TODO: (no rush) use a non copied memory section to + hold an initialization flag. */ +void +pthread_mutex::initMutex () +{ + if (!mutexInitializationLock.init ()) + api_fatal ("Could not create win32 Mutex for pthread mutex static initializer support."); +} + +pthread_mutex::pthread_mutex (pthread_mutexattr *attr) : + verifyable_object (PTHREAD_MUTEX_MAGIC), + lock_counter (MUTEX_LOCK_COUNTER_INITIAL), + win32_obj_id (NULL), recursion_counter (0), + condwaits (0), owner (NULL), type (PTHREAD_MUTEX_DEFAULT), + pshared (PTHREAD_PROCESS_PRIVATE) +{ + win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); + if (!win32_obj_id) + { + magic = 0; + return; + } + /*attr checked in the C call */ + if (attr) + { + if (attr->pshared == PTHREAD_PROCESS_SHARED) + { + // fail + magic = 0; + return; + } + + type = attr->mutextype; + } + + /* threadsafe addition is easy */ + next = (pthread_mutex *) InterlockedExchangePointer (&MT_INTERFACE->mutexs, this); +} + +pthread_mutex::~pthread_mutex () +{ + if (win32_obj_id) + CloseHandle (win32_obj_id); + + /* I'm not 100% sure the next bit is threadsafe. I think it is... */ + if (MT_INTERFACE->mutexs == this) + /* TODO: printf an error if the return value != this */ + InterlockedExchangePointer (&MT_INTERFACE->mutexs, next); + else + { + pthread_mutex *tempmutex = MT_INTERFACE->mutexs; + while (tempmutex->next && tempmutex->next != this) + tempmutex = tempmutex->next; + /* but there may be a race between the loop above and this statement */ + /* TODO: printf an error if the return value != this */ + InterlockedExchangePointer (&tempmutex->next, this->next); + } +} + +int +pthread_mutex::Lock () +{ + int result = 0; + pthread_t self = pthread::self (); + + if (0 == InterlockedIncrement (&lock_counter)) + SetOwner (); + else if (__pthread_equal (&owner, &self)) + { + InterlockedDecrement (&lock_counter); + if (PTHREAD_MUTEX_RECURSIVE == type) + result = LockRecursive (); + else + result = EDEADLK; + } + else + { + WaitForSingleObject (win32_obj_id, INFINITE); + SetOwner (); + } + + return result; +} + +/* returns non-zero on failure */ +int +pthread_mutex::TryLock () +{ + int result = 0; + pthread_t self = pthread::self (); + + if (MUTEX_LOCK_COUNTER_INITIAL == + InterlockedCompareExchange (&lock_counter, 0, MUTEX_LOCK_COUNTER_INITIAL )) + SetOwner (); + else if (__pthread_equal (&owner, &self) && PTHREAD_MUTEX_RECURSIVE == type) + result = LockRecursive (); + else + result = EBUSY; + + return result; +} + +int +pthread_mutex::UnLock () +{ + pthread_t self = pthread::self (); + + if (!__pthread_equal (&owner, &self)) + return EPERM; + + if (0 == --recursion_counter) + { + owner = NULL; + if (MUTEX_LOCK_COUNTER_INITIAL != InterlockedDecrement (&lock_counter)) + // Another thread is waiting + ::ReleaseSemaphore (win32_obj_id, 1, NULL); + } + + return 0; +} + +int +pthread_mutex::Destroy () +{ + if (condwaits || TryLock ()) + // Do not destroy a condwaited or locked mutex + return EBUSY; + else if (recursion_counter != 1) + { + // Do not destroy a recursive locked mutex + --recursion_counter; + return EBUSY; + } + + delete this; + return 0; +} + +void +pthread_mutex::SetOwner () +{ + recursion_counter = 1; + owner = pthread::self (); +} + +int +pthread_mutex::LockRecursive () +{ + if (UINT_MAX == recursion_counter) + return EAGAIN; + ++recursion_counter; + return 0; +} + +void +pthread_mutex::fixup_after_fork () +{ + debug_printf ("mutex %x in fixup_after_fork", this); + if (pshared != PTHREAD_PROCESS_PRIVATE) + api_fatal ("pthread_mutex::fixup_after_fork () doesn'tunderstand PROCESS_SHARED mutex's"); + + if (NULL == owner) + /* mutex has no owner, reset to initial */ + lock_counter = MUTEX_LOCK_COUNTER_INITIAL; + else if (lock_counter != MUTEX_LOCK_COUNTER_INITIAL) + /* All waiting threads are gone after a fork */ + lock_counter = 0; + + win32_obj_id = ::CreateSemaphore (&sec_none_nih, 0, LONG_MAX, NULL); + if (!win32_obj_id) + api_fatal ("pthread_mutex::fixup_after_fork () failed to recreate win32 semaphore for mutex"); + +#if DETECT_BAD_APPS + if (condwaits) + api_fatal ("Forked () while a mutex has condition variables waiting on it.\nReport to cygwin@cygwin.com"); +#else + condwaits = 0; +#endif +} + +bool +pthread_mutexattr::isGoodObject (pthread_mutexattr_t const * attr) +{ + if (verifyable_object_isvalid (attr, PTHREAD_MUTEXATTR_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +pthread_mutexattr::pthread_mutexattr ():verifyable_object (PTHREAD_MUTEXATTR_MAGIC), +pshared (PTHREAD_PROCESS_PRIVATE), mutextype (PTHREAD_MUTEX_DEFAULT) +{ +} + +pthread_mutexattr::~pthread_mutexattr () +{ +} + +semaphore::semaphore (int pshared, unsigned int value):verifyable_object (SEM_MAGIC) +{ + this->win32_obj_id = ::CreateSemaphore (&sec_none_nih, value, LONG_MAX, + NULL); + if (!this->win32_obj_id) + magic = 0; + this->shared = pshared; + currentvalue = value; + /* threadsafe addition is easy */ + next = (semaphore *) InterlockedExchangePointer (&MT_INTERFACE->semaphores, this); +} + +semaphore::~semaphore () +{ + if (win32_obj_id) + CloseHandle (win32_obj_id); + /* I'm not 100% sure the next bit is threadsafe. I think it is... */ + if (MT_INTERFACE->semaphores == this) + InterlockedExchangePointer (&MT_INTERFACE->semaphores, this->next); + else + { + semaphore *tempsem = MT_INTERFACE->semaphores; + while (tempsem->next && tempsem->next != this) + tempsem = tempsem->next; + /* but there may be a race between the loop above and this statement */ + InterlockedExchangePointer (&tempsem->next, this->next); + } +} + +void +semaphore::Post () +{ + /* we can't use the currentvalue, because the wait functions don't let us access it */ + ReleaseSemaphore (win32_obj_id, 1, NULL); + currentvalue++; +} + +int +semaphore::TryWait () +{ + /* FIXME: signals should be able to interrupt semaphores... + *We probably need WaitForMultipleObjects here. + */ + if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT) + { + set_errno (EAGAIN); + return -1; + } + currentvalue--; + return 0; +} + +void +semaphore::Wait () +{ + switch (pthread::cancelable_wait (win32_obj_id, INFINITE)) + { + case WAIT_OBJECT_0: + currentvalue--; + break; + default: + debug_printf ("cancelable_wait failed. %E"); + return; + } +} + +void +semaphore::fixup_after_fork () +{ + debug_printf ("sem %x in fixup_after_fork", this); + if (shared != PTHREAD_PROCESS_PRIVATE) + api_fatal ("doesn't understand PROCESS_SHARED semaphores variables"); + /* FIXME: duplicate code here and in the constructor. */ + this->win32_obj_id = ::CreateSemaphore (&sec_none_nih, currentvalue, LONG_MAX, NULL); + if (!win32_obj_id) + api_fatal ("failed to create new win32 semaphore"); +} + +verifyable_object::verifyable_object (long verifyer): +magic (verifyer) +{ +} + +verifyable_object::~verifyable_object () +{ + magic = 0; +} + +/* Generic memory acccess routine - where should it live ? */ +int __stdcall +check_valid_pointer (void const *pointer) +{ + if (!pointer || IsBadWritePtr ((void *) pointer, sizeof (verifyable_object))) + return EFAULT; + return 0; +} + +verifyable_object_state +verifyable_object_isvalid (void const * objectptr, long magic, void *static_ptr) +{ + verifyable_object **object = (verifyable_object **)objectptr; + if (check_valid_pointer (object)) + return INVALID_OBJECT; + if (static_ptr && *object == static_ptr) + return VALID_STATIC_OBJECT; + if (!*object) + return INVALID_OBJECT; + if (check_valid_pointer (*object)) + return INVALID_OBJECT; + if ((*object)->magic != magic) + return INVALID_OBJECT; + return VALID_OBJECT; +} + +verifyable_object_state +verifyable_object_isvalid (void const * objectptr, long magic) +{ + return verifyable_object_isvalid (objectptr, magic, NULL); +} + +/* Pthreads */ +void * +pthread::thread_init_wrapper (void *_arg) +{ + // Setup the local/global storage of this thread + + pthread *thread = (pthread *) _arg; + struct __reent_t local_reent; + struct _winsup_t local_winsup; + struct _reent local_clib = _REENT_INIT (local_clib); + + struct sigaction _sigs[NSIG]; + sigset_t _sig_mask; /* one set for everything to ignore. */ + LONG _sigtodo[NSIG + __SIGOFFSET]; + + // setup signal structures + thread->sigs = _sigs; + thread->sigmask = &_sig_mask; + thread->sigtodo = _sigtodo; + + memset (&local_winsup, 0, sizeof (struct _winsup_t)); + + local_reent._clib = &local_clib; + local_reent._winsup = &local_winsup; + + local_winsup._process_logmask = LOG_UPTO (LOG_DEBUG); + + MT_INTERFACE->reent_key.set (&local_reent); + + thread->setThreadIdtoCurrent (); + setTlsSelfPointer (thread); + + thread->mutex.Lock (); + // if thread is detached force cleanup on exit + if (thread->attr.joinable == PTHREAD_CREATE_DETACHED && thread->joiner == NULL) + thread->joiner = thread; + thread->mutex.UnLock (); + +#ifdef _CYG_THREAD_FAILSAFE + if (_REENT == _impure_ptr) + system_printf ("local storage for thread isn't setup correctly"); +#endif + + thread_printf ("started thread %p %p %p %p %p %p", _arg, &local_clib, + _impure_ptr, thread, thread->function, thread->arg); + + // call the user's thread + void *ret = thread->function (thread->arg); + + thread->exit (ret); + +#if 0 +// ??? This code only runs if the thread exits by returning. +// it's all now in __pthread_exit (); +#endif + /* never reached */ + return 0; +} + +bool +pthread::isGoodObject (pthread_t const *thread) +{ + if (verifyable_object_isvalid (thread, PTHREAD_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +unsigned long +pthread::getsequence_np () +{ + return getThreadId (); +} + +int +pthread::create (pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + DECLARE_TLS_STORAGE; + if (attr && !pthread_attr::isGoodObject (attr)) + return EINVAL; + + *thread = new pthread (); + (*thread)->create (start_routine, attr ? *attr : NULL, arg); + if (!isGoodObject (thread)) + { + delete (*thread); + *thread = NULL; + return EAGAIN; + } + + return 0; +} + +int +pthread::once (pthread_once_t *once_control, void (*init_routine) (void)) +{ + // already done ? + if (once_control->state) + return 0; + + pthread_mutex_lock (&once_control->mutex); + /* Here we must set a cancellation handler to unlock the mutex if needed */ + /* but a cancellation handler is not the right thing. We need this in the thread + *cleanup routine. Assumption: a thread can only be in one pthread_once routine + *at a time. Stote a mutex_t *in the pthread_structure. if that's non null unlock + *on pthread_exit (); + */ + if (!once_control->state) + { + init_routine (); + once_control->state = 1; + } + /* Here we must remove our cancellation handler */ + pthread_mutex_unlock (&once_control->mutex); + return 0; +} + +int +pthread::cancel (pthread_t thread) +{ + if (!isGoodObject (&thread)) + return ESRCH; + + return thread->cancel (); +} + +/* Races in pthread_atfork: + We are race safe in that any additions to the lists are made via + InterlockedExchangePointer. + However, if the user application doesn't perform syncronisation of some sort + It's not guaranteed that a near simultaneous call to pthread_atfork and fork + will result in the new atfork handlers being calls. + More rigorous internal syncronisation isn't needed as the user program isn't + guaranteeing their own state. + + as far as multiple calls to pthread_atfork, the worst case is simultaneous calls + will result in an indeterminate order for parent and child calls (what gets inserted + first isn't guaranteed.) + + There is one potential race... Does the result of InterlockedExchangePointer + get committed to the return location _before_ any context switches can occur? + If yes, we're safe, if no, we're not. */ +void +pthread::atforkprepare (void) +{ + MT_INTERFACE->fixup_before_fork (); + + callback *cb = MT_INTERFACE->pthread_prepare; + while (cb) + { + cb->cb (); + cb = cb->next; + } +} + +void +pthread::atforkparent (void) +{ + callback *cb = MT_INTERFACE->pthread_parent; + while (cb) + { + cb->cb (); + cb = cb->next; + } +} + +void +pthread::atforkchild (void) +{ + MT_INTERFACE->fixup_after_fork (); + + callback *cb = MT_INTERFACE->pthread_child; + while (cb) + { + cb->cb (); + cb = cb->next; + } +} + +/* Register a set of functions to run before and after fork. + prepare calls are called in LI-FC order. + parent and child calls are called in FI-FC order. */ +int +pthread::atfork (void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + callback *prepcb = NULL, *parentcb = NULL, *childcb = NULL; + if (prepare) + { + prepcb = new callback; + if (!prepcb) + return ENOMEM; + } + if (parent) + { + parentcb = new callback; + if (!parentcb) + { + if (prepcb) + delete prepcb; + return ENOMEM; + } + } + if (child) + { + childcb = new callback; + if (!childcb) + { + if (prepcb) + delete prepcb; + if (parentcb) + delete parentcb; + return ENOMEM; + } + } + + if (prepcb) + { + prepcb->cb = prepare; + prepcb->next = (callback *) InterlockedExchangePointer ((LONG *) &MT_INTERFACE->pthread_prepare, (long int) prepcb); + } + if (parentcb) + { + parentcb->cb = parent; + callback **t = &MT_INTERFACE->pthread_parent; + while (*t) + t = &(*t)->next; + /* t = pointer to last next in the list */ + parentcb->next = (callback *) InterlockedExchangePointer ((LONG *) t, (long int) parentcb); + } + if (childcb) + { + childcb->cb = child; + callback **t = &MT_INTERFACE->pthread_child; + while (*t) + t = &(*t)->next; + /* t = pointer to last next in the list */ + childcb->next = (callback *) InterlockedExchangePointer ((LONG *) t, (long int) childcb); + } + return 0; +} + +int +__pthread_attr_init (pthread_attr_t *attr) +{ + if (check_valid_pointer (attr)) + return EINVAL; + *attr = new pthread_attr; + if (!pthread_attr::isGoodObject (attr)) + { + delete (*attr); + *attr = NULL; + return EAGAIN; + } + return 0; +} + +int +__pthread_attr_getinheritsched (const pthread_attr_t *attr, + int *inheritsched) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + *inheritsched = (*attr)->inheritsched; + return 0; +} + +int +__pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + *param = (*attr)->schedparam; + return 0; +} + +/* From a pure code point of view, this should call a helper in sched.cc, + to allow for someone adding scheduler policy changes to win32 in the future. + However that's extremely unlikely, so short and sweet will do us */ +int +__pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + *policy = SCHED_FIFO; + return 0; +} + + +int +__pthread_attr_getscope (const pthread_attr_t *attr, int *contentionscope) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + *contentionscope = (*attr)->contentionscope; + return 0; +} + +int +__pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + if (detachstate < 0 || detachstate > 1) + return EINVAL; + (*attr)->joinable = detachstate; + return 0; +} + +int +__pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + *detachstate = (*attr)->joinable; + return 0; +} + +int +__pthread_attr_setinheritsched (pthread_attr_t *attr, int inheritsched) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + if (inheritsched != PTHREAD_INHERIT_SCHED + && inheritsched != PTHREAD_EXPLICIT_SCHED) + return ENOTSUP; + (*attr)->inheritsched = inheritsched; + return 0; +} + +int +__pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + if (!valid_sched_parameters (param)) + return ENOTSUP; + (*attr)->schedparam = *param; + return 0; +} + +/* See __pthread_attr_getschedpolicy for some notes */ +int +__pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + if (policy != SCHED_FIFO) + return ENOTSUP; + return 0; +} + +int +__pthread_attr_setscope (pthread_attr_t *attr, int contentionscope) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + if (contentionscope != PTHREAD_SCOPE_SYSTEM + && contentionscope != PTHREAD_SCOPE_PROCESS) + return EINVAL; + /* In future, we may be able to support system scope by escalating the thread + priority to exceed the priority class. For now we only support PROCESS scope. */ + if (contentionscope != PTHREAD_SCOPE_PROCESS) + return ENOTSUP; + (*attr)->contentionscope = contentionscope; + return 0; +} + +int +__pthread_attr_setstacksize (pthread_attr_t *attr, size_t size) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + (*attr)->stacksize = size; + return 0; +} + +int +__pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *size) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + *size = (*attr)->stacksize; + return 0; +} + +int +__pthread_attr_destroy (pthread_attr_t *attr) +{ + if (!pthread_attr::isGoodObject (attr)) + return EINVAL; + delete (*attr); + *attr = NULL; + return 0; +} + +int +pthread::join (pthread_t *thread, void **return_val) +{ + pthread_t joiner = self (); + + joiner->testcancel (); + + // Initialize return val with NULL + if (return_val) + *return_val = NULL; + + if (!isGoodObject (&joiner)) + return EINVAL; + + if (!isGoodObject (thread)) + return ESRCH; + + if (__pthread_equal (thread,&joiner)) + return EDEADLK; + + (*thread)->mutex.Lock (); + + if ((*thread)->attr.joinable == PTHREAD_CREATE_DETACHED) + { + (*thread)->mutex.UnLock (); + return EINVAL; + } + else + { + (*thread)->joiner = joiner; + (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED; + (*thread)->mutex.UnLock (); + + switch (cancelable_wait ((*thread)->win32_obj_id, INFINITE, false)) + { + case WAIT_OBJECT_0: + if (return_val) + *return_val = (*thread)->return_ptr; + delete (*thread); + break; + case WAIT_CANCELED: + // set joined thread back to joinable since we got canceled + (*thread)->joiner = NULL; + (*thread)->attr.joinable = PTHREAD_CREATE_JOINABLE; + joiner->cancel_self (); + // never reached + break; + default: + // should never happen + return EINVAL; + } + } + + return 0; +} + +int +pthread::detach (pthread_t *thread) +{ + if (!isGoodObject (thread)) + return ESRCH; + + (*thread)->mutex.Lock (); + if ((*thread)->attr.joinable == PTHREAD_CREATE_DETACHED) + { + (*thread)->mutex.UnLock (); + return EINVAL; + } + + // check if thread is still alive + if (WAIT_TIMEOUT == WaitForSingleObject ((*thread)->win32_obj_id, 0)) + { + // force cleanup on exit + (*thread)->joiner = *thread; + (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED; + (*thread)->mutex.UnLock (); + } + else + { + // thread has already terminated. + (*thread)->mutex.UnLock (); + delete (*thread); + } + + return 0; +} + +int +pthread::suspend (pthread_t *thread) +{ + if (!isGoodObject (thread)) + return ESRCH; + + if ((*thread)->suspended == false) + { + (*thread)->suspended = true; + SuspendThread ((*thread)->win32_obj_id); + } + + return 0; +} + + +int +pthread::resume (pthread_t *thread) +{ + if (!isGoodObject (thread)) + return ESRCH; + + if ((*thread)->suspended == true) + ResumeThread ((*thread)->win32_obj_id); + (*thread)->suspended = false; + + return 0; +} + +/* provided for source level compatability. + See http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html +*/ +int +__pthread_getconcurrency (void) +{ + return MT_INTERFACE->concurrency; +} + +/* keep this in sync with sched.cc */ +int +__pthread_getschedparam (pthread_t thread, int *policy, + struct sched_param *param) +{ + if (!pthread::isGoodObject (&thread)) + return ESRCH; + *policy = SCHED_FIFO; + /* we don't return the current effective priority, we return the current + requested priority */ + *param = thread->attr.schedparam; + return 0; +} + +/* Thread SpecificData */ +int +__pthread_key_create (pthread_key_t *key, void (*destructor) (void *)) +{ + /* The opengroup docs don't define if we should check this or not, + but creation is relatively rare. */ + if (pthread_key::isGoodObject (key)) + return EBUSY; + + *key = new pthread_key (destructor); + + if (!pthread_key::isGoodObject (key)) + { + delete (*key); + *key = NULL; + return EAGAIN; + } + return 0; +} + +int +__pthread_key_delete (pthread_key_t key) +{ + if (!pthread_key::isGoodObject (&key)) + return EINVAL; + + delete (key); + return 0; +} + +/* provided for source level compatability. See +http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_getconcurrency.html +*/ +int +__pthread_setconcurrency (int new_level) +{ + if (new_level < 0) + return EINVAL; + MT_INTERFACE->concurrency = new_level; + return 0; +} + +/* keep syncronised with sched.cc */ +int +__pthread_setschedparam (pthread_t thread, int policy, + const struct sched_param *param) +{ + if (!pthread::isGoodObject (&thread)) + return ESRCH; + if (policy != SCHED_FIFO) + return ENOTSUP; + if (!param) + return EINVAL; + int rv = + sched_set_thread_priority (thread->win32_obj_id, param->sched_priority); + if (!rv) + thread->attr.schedparam.sched_priority = param->sched_priority; + return rv; +} + + +int +__pthread_setspecific (pthread_key_t key, const void *value) +{ + if (!pthread_key::isGoodObject (&key)) + return EINVAL; + (key)->set (value); + return 0; +} + +void * +__pthread_getspecific (pthread_key_t key) +{ + if (!pthread_key::isGoodObject (&key)) + return NULL; + + return (key)->get (); + +} + +/* Thread synchronisation */ +bool +pthread_cond::isGoodObject (pthread_cond_t const *cond) +{ + if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +bool +pthread_cond::isGoodInitializer (pthread_cond_t const *cond) +{ + if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER) != VALID_STATIC_OBJECT) + return false; + return true; +} + +bool +pthread_cond::isGoodInitializerOrObject (pthread_cond_t const *cond) +{ + if (verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER) == INVALID_OBJECT) + return false; + return true; +} + +bool +pthread_cond::isGoodInitializerOrBadObject (pthread_cond_t const *cond) +{ + verifyable_object_state objectState = verifyable_object_isvalid (cond, PTHREAD_COND_MAGIC, PTHREAD_COND_INITIALIZER); + if (objectState == VALID_OBJECT) + return false; + return true; +} + +int +__pthread_cond_destroy (pthread_cond_t *cond) +{ + if (pthread_cond::isGoodInitializer (cond)) + return 0; + if (!pthread_cond::isGoodObject (cond)) + return EINVAL; + + /* reads are atomic */ + if ((*cond)->waiting) + return EBUSY; + + delete (*cond); + *cond = NULL; + + return 0; +} + +int +pthread_cond::init (pthread_cond_t *cond, const pthread_condattr_t *attr) +{ + if (attr && !pthread_condattr::isGoodObject (attr)) + return EINVAL; + if (!condInitializationLock.lock ()) + return EINVAL; + + if (!isGoodInitializerOrBadObject (cond)) + { + condInitializationLock.unlock (); + return EBUSY; + } + + *cond = new pthread_cond (attr ? (*attr) : NULL); + if (!isGoodObject (cond)) + { + delete (*cond); + *cond = NULL; + condInitializationLock.unlock (); + return EAGAIN; + } + condInitializationLock.unlock (); + return 0; +} + +int +__pthread_cond_broadcast (pthread_cond_t *cond) +{ + if (pthread_cond::isGoodInitializer (cond)) + pthread_cond::init (cond, NULL); + if (!pthread_cond::isGoodObject (cond)) + return EINVAL; + + (*cond)->BroadCast (); + + return 0; +} + +int +__pthread_cond_signal (pthread_cond_t *cond) +{ + if (pthread_cond::isGoodInitializer (cond)) + pthread_cond::init (cond, NULL); + if (!pthread_cond::isGoodObject (cond)) + return EINVAL; + + (*cond)->Signal (); + + return 0; +} + +int +__pthread_cond_dowait (pthread_cond_t *cond, pthread_mutex_t *mutex, + long waitlength) +{ +// and yes cond_access here is still open to a race. (we increment, context swap, +// broadcast occurs - we miss the broadcast. the functions aren't split properly. + int rv; + pthread_mutex **themutex = NULL; + if (pthread_mutex::isGoodInitializer (mutex)) + pthread_mutex::init (mutex, NULL); + themutex = mutex; + if (pthread_cond::isGoodInitializer (cond)) + pthread_cond::init (cond, NULL); + + if (!pthread_mutex::isGoodObject (themutex)) + return EINVAL; + if (!pthread_cond::isGoodObject (cond)) + return EINVAL; + + /* if the cond variable is blocked, then the above timer test maybe wrong. *shrug**/ + if (pthread_mutex_lock (&(*cond)->cond_access)) + system_printf ("Failed to lock condition variable access mutex, this %p", *cond); + + if ((*cond)->waiting) + if ((*cond)->mutex && ((*cond)->mutex != (*themutex))) + { + if (pthread_mutex_unlock (&(*cond)->cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", *cond); + return EINVAL; + } + InterlockedIncrement (&((*cond)->waiting)); + + (*cond)->mutex = (*themutex); + InterlockedIncrement (&((*themutex)->condwaits)); + if (pthread_mutex_unlock (&(*cond)->cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", *cond); + /* At this point calls to Signal will progress evebn if we aren' yet waiting + However, the loop there should allow us to get scheduled and call wait, + and have them call PulseEvent again if we dont' respond. */ + rv = (*cond)->TimedWait (waitlength); + /* this may allow a race on the mutex acquisition and waits. + But doing this within the cond access mutex creates a different race */ + InterlockedDecrement (&((*cond)->waiting)); + /* Tell Signal that we have been released */ + InterlockedDecrement (&((*cond)->ExitingWait)); + (*themutex)->Lock (); + if (pthread_mutex_lock (&(*cond)->cond_access)) + system_printf ("Failed to lock condition variable access mutex, this %p", *cond); + if ((*cond)->waiting == 0) + (*cond)->mutex = NULL; + InterlockedDecrement (&((*themutex)->condwaits)); + if (pthread_mutex_unlock (&(*cond)->cond_access)) + system_printf ("Failed to unlock condition variable access mutex, this %p", *cond); + + return rv; +} + +extern "C" int +pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + if (check_valid_pointer (abstime)) + return EINVAL; + struct timeb currSysTime; + long waitlength; + ftime (&currSysTime); + waitlength = (abstime->tv_sec - currSysTime.time) * 1000; + if (waitlength < 0) + return ETIMEDOUT; + return __pthread_cond_dowait (cond, mutex, waitlength); +} + +extern "C" int +pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + return __pthread_cond_dowait (cond, mutex, INFINITE); +} + +int +__pthread_condattr_init (pthread_condattr_t *condattr) +{ + if (check_valid_pointer (condattr)) + return EINVAL; + *condattr = new pthread_condattr; + if (!pthread_condattr::isGoodObject (condattr)) + { + delete (*condattr); + *condattr = NULL; + return EAGAIN; + } + return 0; +} + +int +__pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared) +{ + if (!pthread_condattr::isGoodObject (attr)) + return EINVAL; + *pshared = (*attr)->shared; + return 0; +} + +int +__pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared) +{ + if (!pthread_condattr::isGoodObject (attr)) + return EINVAL; + if ((pshared < 0) || (pshared > 1)) + return EINVAL; + /* shared cond vars not currently supported */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + (*attr)->shared = pshared; + return 0; +} + +int +__pthread_condattr_destroy (pthread_condattr_t *condattr) +{ + if (!pthread_condattr::isGoodObject (condattr)) + return EINVAL; + delete (*condattr); + *condattr = NULL; + return 0; +} + +/* Thread signal */ +int +__pthread_kill (pthread_t thread, int sig) +{ + // lock myself, for the use of thread2signal + // two different kills might clash: FIXME + + if (!pthread::isGoodObject (&thread)) + return EINVAL; + + if (thread->sigs) + myself->setthread2signal (thread); + + int rval = raise (sig); + + // unlock myself + return rval; +} + +int +__pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set) +{ + pthread *thread = pthread::self (); + + // lock this myself, for the use of thread2signal + // two differt kills might clash: FIXME + + if (thread->sigs) + myself->setthread2signal (thread); + + int rval = sigprocmask (operation, set, old_set); + + // unlock this myself + + return rval; +} + +/* ID */ + +int +__pthread_equal (pthread_t *t1, pthread_t *t2) +{ + return (*t1 == *t2); +} + +/* Mutexes */ + +/* FIXME: there's a potential race with PTHREAD_MUTEX_INITALIZER: + the mutex is not actually inited until the first use. + So two threads trying to lock/trylock may collide. + Solution: we need a global mutex on mutex creation, or possibly simply + on all constructors that allow INITIALIZER macros. + the lock should be very small: only around the init routine, not + every test, or all mutex access will be synchronised. */ + +int +pthread_mutex::init (pthread_mutex_t *mutex, + const pthread_mutexattr_t *attr) +{ + if (attr && !pthread_mutexattr::isGoodObject (attr) || check_valid_pointer (mutex)) + return EINVAL; + if (!mutexInitializationLock.lock ()) + return EINVAL; + + if (!isGoodInitializerOrBadObject (mutex)) + { + mutexInitializationLock.unlock (); + return EBUSY; + } + + *mutex = new pthread_mutex (attr ? (*attr) : NULL); + if (!isGoodObject (mutex)) + { + delete (*mutex); + *mutex = NULL; + mutexInitializationLock.unlock (); + return EAGAIN; + } + mutexInitializationLock.unlock (); + return 0; +} + +int +__pthread_mutex_getprioceiling (const pthread_mutex_t *mutex, + int *prioceiling) +{ + pthread_mutex_t *themutex = (pthread_mutex_t *) mutex; + if (pthread_mutex::isGoodInitializer (mutex)) + pthread_mutex::init ((pthread_mutex_t *) mutex, NULL); + if (!pthread_mutex::isGoodObject (themutex)) + return EINVAL; + /* We don't define _POSIX_THREAD_PRIO_PROTECT because we do't currently support + mutex priorities. + + We can support mutex priorities in the future though: + Store a priority with each mutex. + When the mutex is optained, set the thread priority as appropriate + When the mutex is released, reset the thread priority. */ + return ENOSYS; +} + +int +__pthread_mutex_lock (pthread_mutex_t *mutex) +{ + pthread_mutex_t *themutex = mutex; + /* This could be simplified via isGoodInitializerOrObject + and isGoodInitializer, but in a performance critical call like this.... + no. */ + switch (verifyable_object_isvalid (themutex, PTHREAD_MUTEX_MAGIC, PTHREAD_MUTEX_INITIALIZER)) + { + case INVALID_OBJECT: + return EINVAL; + break; + case VALID_STATIC_OBJECT: + if (pthread_mutex::isGoodInitializer (mutex)) + { + int rv = pthread_mutex::init (mutex, NULL); + if (rv && rv != EBUSY) + return rv; + } + /* No else needed. If it's been initialized while we waited, + we can just attempt to lock it */ + break; + case VALID_OBJECT: + break; + } + return (*themutex)->Lock (); +} + +int +__pthread_mutex_trylock (pthread_mutex_t *mutex) +{ + pthread_mutex_t *themutex = mutex; + if (pthread_mutex::isGoodInitializer (mutex)) + pthread_mutex::init (mutex, NULL); + if (!pthread_mutex::isGoodObject (themutex)) + return EINVAL; + return (*themutex)->TryLock (); +} + +int +__pthread_mutex_unlock (pthread_mutex_t *mutex) +{ + if (pthread_mutex::isGoodInitializer (mutex)) + pthread_mutex::init (mutex, NULL); + if (!pthread_mutex::isGoodObject (mutex)) + return EINVAL; + return (*mutex)->UnLock (); +} + +int +__pthread_mutex_destroy (pthread_mutex_t *mutex) +{ + int rv; + + if (pthread_mutex::isGoodInitializer (mutex)) + return 0; + if (!pthread_mutex::isGoodObject (mutex)) + return EINVAL; + + rv = (*mutex)->Destroy (); + if (rv) + return rv; + + *mutex = NULL; + return 0; +} + +int +__pthread_mutex_setprioceiling (pthread_mutex_t *mutex, int prioceiling, + int *old_ceiling) +{ + pthread_mutex_t *themutex = mutex; + if (pthread_mutex::isGoodInitializer (mutex)) + pthread_mutex::init (mutex, NULL); + if (!pthread_mutex::isGoodObject (themutex)) + return EINVAL; + return ENOSYS; +} + +/* Win32 doesn't support mutex priorities - see __pthread_mutex_getprioceiling + for more detail */ +int +__pthread_mutexattr_getprotocol (const pthread_mutexattr_t *attr, + int *protocol) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + return ENOSYS; +} + +int +__pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, + int *pshared) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + *pshared = (*attr)->pshared; + return 0; +} + +int +__pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + *type = (*attr)->mutextype; + return 0; +} + +/* FIXME: write and test process shared mutex's. */ +int +__pthread_mutexattr_init (pthread_mutexattr_t *attr) +{ + if (pthread_mutexattr::isGoodObject (attr)) + return EBUSY; + + *attr = new pthread_mutexattr (); + if (!pthread_mutexattr::isGoodObject (attr)) + { + delete (*attr); + *attr = NULL; + return ENOMEM; + } + return 0; +} + +int +__pthread_mutexattr_destroy (pthread_mutexattr_t *attr) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + delete (*attr); + *attr = NULL; + return 0; +} + + +/* Win32 doesn't support mutex priorities */ +int +__pthread_mutexattr_setprotocol (pthread_mutexattr_t *attr, int protocol) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + return ENOSYS; +} + +/* Win32 doesn't support mutex priorities */ +int +__pthread_mutexattr_setprioceiling (pthread_mutexattr_t *attr, + int prioceiling) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + return ENOSYS; +} + +int +__pthread_mutexattr_getprioceiling (const pthread_mutexattr_t *attr, + int *prioceiling) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + return ENOSYS; +} + +int +__pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + /* we don't use pshared for anything as yet. We need to test PROCESS_SHARED + *functionality + */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return EINVAL; + (*attr)->pshared = pshared; + return 0; +} + +/* see __pthread_mutex_gettype */ +int +__pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type) +{ + if (!pthread_mutexattr::isGoodObject (attr)) + return EINVAL; + + switch (type) + { + case PTHREAD_MUTEX_ERRORCHECK: + case PTHREAD_MUTEX_RECURSIVE: + (*attr)->mutextype = type; + break; + default: + return EINVAL; + } + + return 0; +} + +/* Semaphores */ + +/* static members */ +bool +semaphore::isGoodObject (sem_t const * sem) +{ + if (verifyable_object_isvalid (sem, SEM_MAGIC) != VALID_OBJECT) + return false; + return true; +} + +int +semaphore::init (sem_t *sem, int pshared, unsigned int value) +{ + /* opengroup calls this undefined */ + if (isGoodObject (sem)) + return EBUSY; + + if (value > SEM_VALUE_MAX) + return EINVAL; + + *sem = new semaphore (pshared, value); + + if (!isGoodObject (sem)) + { + delete (*sem); + *sem = NULL; + return EAGAIN; + } + return 0; +} + +int +semaphore::destroy (sem_t *sem) +{ + if (!isGoodObject (sem)) + return EINVAL; + + /* FIXME - new feature - test for busy against threads... */ + + delete (*sem); + *sem = NULL; + return 0; +} + +int +semaphore::wait (sem_t *sem) +{ + pthread_testcancel (); + + if (!isGoodObject (sem)) + { + set_errno (EINVAL); + return -1; + } + + (*sem)->Wait (); + return 0; +} + +int +semaphore::trywait (sem_t *sem) +{ + if (!isGoodObject (sem)) + { + set_errno (EINVAL); + return -1; + } + + return (*sem)->TryWait (); +} + +int +semaphore::post (sem_t *sem) +{ + if (!isGoodObject (sem)) + return EINVAL; + + (*sem)->Post (); + return 0; +} + +/* pthreadNull */ +pthread * +pthreadNull::getNullpthread () +{ + /* because of weird entry points */ + _instance.magic = 0; + return &_instance; +} + +pthreadNull::pthreadNull () +{ + attr.joinable = PTHREAD_CREATE_DETACHED; + /* Mark ourselves as invalid */ + magic = 0; +} + +pthreadNull::~pthreadNull () +{ +} + +void +pthreadNull::create (void *(*)(void *), pthread_attr *, void *) +{ +} + +void +pthreadNull::exit (void *value_ptr) +{ + ExitThread (0); +} + +int +pthreadNull::cancel () +{ + return 0; +} + +void +pthreadNull::testcancel () +{ +} + +int +pthreadNull::setcancelstate (int state, int *oldstate) +{ + return EINVAL; +} + +int +pthreadNull::setcanceltype (int type, int *oldtype) +{ + return EINVAL; +} + +void +pthreadNull::push_cleanup_handler (__pthread_cleanup_handler *handler) +{ +} + +void +pthreadNull::pop_cleanup_handler (int const execute) +{ +} + +unsigned long +pthreadNull::getsequence_np () +{ + return 0; +} + +pthreadNull pthreadNull::_instance; + +#endif // MT_SAFE diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index b9417d18da9..bb644b2e5d3 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -82,12 +82,18 @@ create_tty_master (int ttynum) { /* Log utmp entry */ struct utmp our_utmp; + DWORD len = sizeof our_utmp.ut_host; bzero ((char *) &our_utmp, sizeof (utmp)); (void) time (&our_utmp.ut_time); strncpy (our_utmp.ut_name, getlogin (), sizeof (our_utmp.ut_name)); - cygwin_gethostname (our_utmp.ut_host, sizeof (our_utmp.ut_host)); + GetComputerName (our_utmp.ut_host, &len); __small_sprintf (our_utmp.ut_line, "tty%d", ttynum); + if ((len = strlen (our_utmp.ut_line)) >= UT_IDLEN) + len -= UT_IDLEN; + else + len = 0; + strncpy (our_utmp.ut_id, our_utmp.ut_line + len, UT_IDLEN); our_utmp.ut_type = USER_PROCESS; our_utmp.ut_pid = myself->pid; myself->ctty = ttynum; |