diff options
33 files changed, 4789 insertions, 2626 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index fa3cfa23546..a1e8650512c 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,154 @@ +2003-09-01 Christopher Faylor <cgf@redhat.com> + + * include/cygwin/version.h: Bump DLL minor number to 4. + +2003-09-01 Christopher Faylor <cgf@redhat.com> + + * net.cc (dup_ent): Restore check for NULL input. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * include/sys/cygwin.h: Don't define cygwin-specific things if + !__CYGWIN__. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * cygheap.cc (cygheap_init): Allocate space for sigaction array in + cygheap. + * cygheap.h (cygheap_types): Add HEAP_SIGS. + * exceptions.cc (signal_fixup_after_exec): Remove from this file. + * pinfo.h (pinfo::getsig): Just return global_sigs array. + (pinfo::sigs): Delete. + * sigproc.cc (signal_fixup_after_exec): Move it here. + (global_sigs): New global array, moved from pinfo structure. + (sigalloc): New function. Allocate global sigaction array here. + (proc_subproc): Remove copysigs call. It's automatic now. + * include/sys/cygwin.h (PID_NOCLDSTOP): New value. + * signal.cc (sigaction): Set myself->PID_NODCLDSTOP when appropriate. + * sigproc.h (sigalloc): Declare. + + * fnmatch.c (fnmatch): Use C90 parameters. + (rangematch): Ditto. + + * fhandler.cc (fhandler_base::raw_read): Use right coercion to avoid a + compiler warning. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * net.cc (dup_ent): Make debugging output consistent. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + Use dup_ent rather than specific dup_*_ptr functions throughout. + * (gen_ent): Delete. + (dup_ent): Subsume gen_ent functionality. + (dup_host_ptr): Delete. + (dup_proto_ptr): Ditto. + (dup_servent_ptr): Ditto. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * net.cc (gen_ent): Invert sense of null check so that debug output + makes sense. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * net.cc (free_char_list): Delete. + (dup_addr_list): Delete. + (dup_char_list): Delete. + (free_hostent_ptr): Delete. + (free_protoent_ptr): Delete. + (free_servent_ptr): Delete. + (DWORD_round): New function. + (strlen_round): New function. Returns strlen rounded up to word size. + (dup_ent): New, generic function to duplicate a {host,proto,serv}ent + structure. + (gen_ent): New macro. Generates a generic dup_{host,proto,serv}ent_ptr + function. + (cygwin_getservbyname): Remove call to free_servent_ptr, pass + servent_buf to dup_servent_ptr. + (cygwin_getservbyport): Ditto. + (cygwin_gethostbyname): Ditto for hostent. + (cygwin_gethostbyaddr): Ditto. + (cygwin_getprotobyname): Ditto for protoent. + (cygwin_getprotobynumber): Ditto. + +2003-08-31 Christopher Faylor <cgf@redhat.com> + + * Makefile.in (MALLOC_OFILES): Always fill in with correct malloc + object. + * configure.in: Fill in MALLOC_OFILES with either debugging or regular + malloc. + * configure: Regenerate. + * dlmalloc.c: Make various fruitless changes to attempt to get to work. + * dlmalloc.h: Ditto. + * malloc.cc (free): Check malloc pool when debugging. + + * path.cc (win32_device_name): Eliminate compiler warning. + + * sigproc.cc (sig_dispatch_pending): Remove use of was_pending. Let + thisframe.call_signal_handler decide if handler should be called rather + than using bogus was_pending check. + + * exceptions.cc (interrupt_setup): Remove accidentally checked in + debugging code. + +2003-08-30 Christopher Faylor <cgf@redhat.com> + + * heap.cc (sbrk): Save rounded addess in user_heap_max. + +2003-08-30 Christopher Faylor <cgf@redhat.com> + + * sigproc.cc (sig_dispatch_pending): Remove explicit call to + thisframe.call_signal_handler. + +2003-08-30 Christopher Faylor <cgf@redhat.com> + + Remove some cygserver files. + +2003-08-28 Christopher Faylor <cgf@redhat.com> + + * sigproc.h: Make some functions regparm. + * sigproc.cc (checkstate): Make regparm. + (getevent): Change parameters in declaration, rename from getsem, make regparm. + (sig_send): Recognize that nosync is now an event. Remove some old + cruft from previous interrupt anywhere signal handler. + (getevent): Change parameters in definition, rename from getsem. + Allocate event rather than semaphore. + (wait_sig): Treat sigcatch_nosync as an event. + +2003-08-28 Christopher Faylor <cgf@redhat.com> + + * exceptions.cc (sigreturn): Fix problem where old return address was + not properly restored for a nested signal. + +2003-08-27 Christopher Faylor <cgf@redhat.com> + + * autoload.cc (SwitchToThread): Declare as autoload function. + * cygthread.h (cygthread::main_thread_id): Make public. + * exceptions.cc (setup_handler): Remove unneeded priority stuff. + Rename label to reflect what it does. Add debugging for idiotic + Windows NT problem. Change debugging output to include signal number. + * miscfuncs.cc (low_priority_sleep): If available, use SwitchToThread + function to give time slice to other threads. + * wincap.cc: Properly define have_switch_to_thread throughout. + * wincap.h (wincap::switch_to_thread): New element. + +2003-08-27 Christopher Faylor <cgf@redhat.com> + + * syscalls.cc (mount): Don't check win32_path when doing cygdrive + mount. + +2003-08-27 Christopher Faylor <cgf@redhat.com> + + * specdir: Correctly remove temporary directory prior to use. + +2003-08-27 Christopher Faylor <cgf@redhat.com> + + * sigproc.cc (wait_sig): Count number of iterations through + 'more_signals' loop and issue a warning if DEBUGGING and excessive. + (WFSO): When debugging and infinite timeout, loop. + 2003-08-26 Corinna Vinschen <corinna@vinschen.de> * include/cygwin/stat.h: Allow definition of internal stat structures diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index cd021353231..d0fc32c7c40 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -154,7 +154,7 @@ DLL_OFILES:=assert.o autoload.o cxx.o cygheap.o cygthread.o dcrt0.o debug.o \ fhandler_socket.o fhandler_tape.o fhandler_termios.o \ fhandler_tty.o fhandler_virtual.o fhandler_windows.o \ fhandler_zero.o fnmatch.o fork.o glob.o grp.o heap.o init.o ioctl.o \ - ipc.o localtime.o malloc.o malloc_wrapper.o miscfuncs.o mmap.o msg.o \ + ipc.o localtime.o malloc_wrapper.o miscfuncs.o mmap.o msg.o \ net.o netdb.o ntea.o passwd.o path.o pinfo.o pipe.o poll.o pthread.o \ regcomp.o regerror.o regexec.o regfree.o registry.o resource.o \ scandir.o sched.o sec_acl.o sec_helper.o security.o select.o sem.o \ diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index f5cc250fa0e..c2c7ae3528b 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -510,6 +510,7 @@ LoadDLLfunc (IsProcessorFeaturePresent, 4, kernel32); LoadDLLfuncEx (Process32First, 8, kernel32, 1) LoadDLLfuncEx (Process32Next, 8, kernel32, 1) LoadDLLfuncEx (SignalObjectAndWait, 16, kernel32, 1) +LoadDLLfuncEx (SwitchToThread, 0, kernel32, 1) LoadDLLfunc (TryEnterCriticalSection, 4, kernel32) LoadDLLfuncEx (waveOutGetNumDevs, 0, winmm, 1) diff --git a/winsup/cygwin/configure b/winsup/cygwin/configure index 3500fad387c..eb1b6889a1b 100755 --- a/winsup/cygwin/configure +++ b/winsup/cygwin/configure @@ -1916,7 +1916,7 @@ esac fi -MALLOC_OFILES= +MALLOC_OFILES=malloc.o # Check whether --enable-malloc-debugging or --disable-malloc-debugging was given. if test "${enable_malloc_debugging+set}" = set; then enableval="$enable_malloc_debugging" diff --git a/winsup/cygwin/configure.in b/winsup/cygwin/configure.in index c0ad97682dc..d47b4084fda 100644 --- a/winsup/cygwin/configure.in +++ b/winsup/cygwin/configure.in @@ -145,7 +145,7 @@ no) LIBSERVER=;; esac ]) -MALLOC_OFILES= +MALLOC_OFILES=malloc.o AC_ARG_ENABLE(malloc-debugging, [ --enable-malloc-debugging Build a cygwin DLL with heap sanity checking (this is very slow, use only if you have heap corruption problems)], [case "${enableval}" in diff --git a/winsup/cygwin/cygheap.cc b/winsup/cygwin/cygheap.cc index c18232eed82..f681a5ca2b3 100644 --- a/winsup/cygwin/cygheap.cc +++ b/winsup/cygwin/cygheap.cc @@ -22,6 +22,7 @@ #include "heap.h" #include "sync.h" #include "shared_info.h" +#include "sigproc.h" init_cygheap NO_COPY *cygheap; void NO_COPY *cygheap_max; @@ -203,6 +204,8 @@ cygheap_init () } if (!cygheap->fdtab) cygheap->fdtab.init (); + if (!cygheap->sigs) + sigalloc (); } /* Copyright (C) 1997, 2000 DJ Delorie */ diff --git a/winsup/cygwin/cygheap.h b/winsup/cygwin/cygheap.h index 0d30f1cceca..6939507a53c 100644 --- a/winsup/cygwin/cygheap.h +++ b/winsup/cygwin/cygheap.h @@ -17,6 +17,7 @@ enum cygheap_types HEAP_ARGV, HEAP_BUF, HEAP_MOUNT, + HEAP_SIGS, HEAP_1_START, HEAP_1_STR, HEAP_1_ARGV, @@ -255,6 +256,7 @@ struct init_cygheap #ifdef DEBUGGING cygheap_debug debug; #endif + struct sigaction *sigs; }; #define CYGHEAPSIZE (sizeof (init_cygheap) + (16000 * sizeof (fhandler_union)) + (5 * 65536)) diff --git a/winsup/cygwin/cygserver.cc b/winsup/cygwin/cygserver.cc deleted file mode 100755 index 137730f9ef7..00000000000 --- a/winsup/cygwin/cygserver.cc +++ /dev/null @@ -1,773 +0,0 @@ -/* cygserver.cc - - Copyright 2001, 2002 Red Hat Inc. - - Written by Egor Duda <deo@logos-m.ru> - -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. */ - -#include "woutsup.h" - -#include <sys/types.h> - -#include <assert.h> -#include <ctype.h> -#include <getopt.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "cygerrno.h" -#include "cygwin_version.h" - -#include "cygwin/cygserver.h" -#include "cygwin/cygserver_process.h" -#include "cygwin/cygserver_transport.h" - -// Version string. -static const char version[] = "$Revision$"; - -/* - * Support function for the XXX_printf () macros in "woutsup.h". - * Copied verbatim from "strace.cc". - */ -static int -getfunc (char *in_dst, const char *func) -{ - const char *p; - const char *pe; - char *dst = in_dst; - for (p = func; (pe = strchr (p, '(')); p = pe + 1) - if (isalnum ((int)pe[-1]) || pe[-1] == '_') - break; - else if (isspace ((int)pe[-1])) - { - pe--; - break; - } - if (!pe) - pe = strchr (func, '\0'); - for (p = pe; p > func; p--) - if (p != pe && *p == ' ') - { - p++; - break; - } - if (*p == '*') - p++; - while (p < pe) - *dst++ = *p++; - - *dst++ = ':'; - *dst++ = ' '; - *dst = '\0'; - - return dst - in_dst; -} - -/* - * Support function for the XXX_printf () macros in "woutsup.h". - */ -extern "C" void -__cygserver__printf (const char *const function, const char *const fmt, ...) -{ - const DWORD lasterror = GetLastError (); - const int lasterrno = errno; - - va_list ap; - - char *const buf = (char *) alloca (BUFSIZ); - - assert (buf); - - int len = 0; - - if (function) - len += getfunc (buf, function); - - va_start (ap, fmt); - len += vsnprintf (buf + len, BUFSIZ - len, fmt, ap); - va_end (ap); - - len += snprintf (buf + len, BUFSIZ - len, "\n"); - - const int actual = (len > BUFSIZ ? BUFSIZ : len); - - write (2, buf, actual); - - errno = lasterrno; - SetLastError (lasterror); - - return; -} - -#ifdef DEBUGGING - -int __stdcall -__set_errno (const char *func, int ln, int val) -{ - debug_printf ("%s:%d val %d", func, ln, val); - return _impure_ptr->_errno = val; -} - -#endif /* DEBUGGING */ - -GENERIC_MAPPING access_mapping; - -static BOOL -setup_privileges () -{ - BOOL rc, ret_val; - HANDLE hToken = NULL; - TOKEN_PRIVILEGES sPrivileges; - - rc = OpenProcessToken (GetCurrentProcess () , TOKEN_ALL_ACCESS , &hToken) ; - if (!rc) - { - system_printf ("error opening process token (%lu)", GetLastError ()); - ret_val = FALSE; - goto out; - } - rc = LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &sPrivileges.Privileges[0].Luid); - if (!rc) - { - system_printf ("error getting privilege luid (%lu)", GetLastError ()); - ret_val = FALSE; - goto out; - } - sPrivileges.PrivilegeCount = 1 ; - sPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED ; - rc = AdjustTokenPrivileges (hToken, FALSE, &sPrivileges, 0, NULL, NULL) ; - if (!rc) - { - system_printf ("error adjusting privilege level. (%lu)", - GetLastError ()); - ret_val = FALSE; - goto out; - } - - access_mapping.GenericRead = FILE_READ_DATA; - access_mapping.GenericWrite = FILE_WRITE_DATA; - access_mapping.GenericExecute = 0; - access_mapping.GenericAll = FILE_READ_DATA | FILE_WRITE_DATA; - - ret_val = TRUE; - -out: - CloseHandle (hToken); - return ret_val; -} - -int -check_and_dup_handle (HANDLE from_process, HANDLE to_process, - HANDLE from_process_token, - DWORD access, - HANDLE from_handle, - HANDLE *to_handle_ptr, BOOL bInheritHandle = FALSE) -{ - HANDLE local_handle = NULL; - int ret_val = EACCES; - - if (from_process != GetCurrentProcess ()) - { - if (!DuplicateHandle (from_process, from_handle, - GetCurrentProcess (), &local_handle, - 0, bInheritHandle, - DUPLICATE_SAME_ACCESS)) - { - system_printf ("error getting handle(%u) to server (%lu)", - (unsigned int)from_handle, GetLastError ()); - goto out; - } - } else - local_handle = from_handle; - - if (!wincap.has_security ()) - assert (!from_process_token); - else - { - char sd_buf [1024]; - PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR) &sd_buf; - DWORD bytes_needed; - PRIVILEGE_SET ps; - DWORD ps_len = sizeof (ps); - BOOL status; - - if (!GetKernelObjectSecurity (local_handle, - (OWNER_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | DACL_SECURITY_INFORMATION), - sd, sizeof (sd_buf), &bytes_needed)) - { - system_printf ("error getting handle SD (%lu)", GetLastError ()); - goto out; - } - - MapGenericMask (&access, &access_mapping); - - if (!AccessCheck (sd, from_process_token, access, &access_mapping, - &ps, &ps_len, &access, &status)) - { - system_printf ("error checking access rights (%lu)", - GetLastError ()); - goto out; - } - - if (!status) - { - system_printf ("access to object denied"); - goto out; - } - } - - if (!DuplicateHandle (from_process, from_handle, - to_process, to_handle_ptr, - access, bInheritHandle, 0)) - { - system_printf ("error getting handle to client (%lu)", GetLastError ()); - goto out; - } - - // verbose: debug_printf ("Duplicated %p to %p", from_handle, *to_handle_ptr); - - ret_val = 0; - - out: - if (local_handle && from_process != GetCurrentProcess ()) - CloseHandle (local_handle); - - return (ret_val); -} - -/* - * client_request_attach_tty::serve () - */ - -void -client_request_attach_tty::serve (transport_layer_base *const conn, - process_cache *) -{ - assert (conn); - - assert (!error_code ()); - - if (!wincap.has_security ()) - { - syscall_printf ("operation only supported on systems with security"); - error_code (EINVAL); - msglen (0); - return; - } - - if (msglen () != sizeof (req)) - { - syscall_printf ("bad request body length: expecting %lu bytes, got %lu", - sizeof (req), msglen ()); - error_code (EINVAL); - msglen (0); - return; - } - - msglen (0); // Until we fill in some fields. - - // verbose: debug_printf ("pid %ld:(%p,%p) -> pid %ld", - // req.master_pid, req.from_master, req.to_master, - // req.pid); - - // verbose: debug_printf ("opening process %ld", req.master_pid); - - const HANDLE from_process_handle = - OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.master_pid); - - if (!from_process_handle) - { - system_printf ("error opening `from' process, error = %lu", - GetLastError ()); - error_code (EACCES); - return; - } - - // verbose: debug_printf ("opening process %ld", req.pid); - - const HANDLE to_process_handle = - OpenProcess (PROCESS_DUP_HANDLE, FALSE, req.pid); - - if (!to_process_handle) - { - system_printf ("error opening `to' process, error = %lu", - GetLastError ()); - CloseHandle (from_process_handle); - error_code (EACCES); - return; - } - - // verbose: debug_printf ("Impersonating client"); - conn->impersonate_client (); - - HANDLE token_handle = NULL; - - // verbose: debug_printf ("about to open thread token"); - const DWORD rc = OpenThreadToken (GetCurrentThread (), - TOKEN_QUERY, - TRUE, - &token_handle); - - // verbose: debug_printf ("opened thread token, rc=%lu", rc); - conn->revert_to_self (); - - if (!rc) - { - system_printf ("error opening thread token, error = %lu", - GetLastError ()); - CloseHandle (from_process_handle); - CloseHandle (to_process_handle); - error_code (EACCES); - return; - } - - // From this point on, a reply body is returned to the client. - - const HANDLE from_master = req.from_master; - const HANDLE to_master = req.to_master; - - req.from_master = NULL; - req.to_master = NULL; - - msglen (sizeof (req)); - - if (from_master) - if (check_and_dup_handle (from_process_handle, to_process_handle, - token_handle, - GENERIC_READ, - from_master, - &req.from_master, TRUE) != 0) - { - system_printf ("error duplicating from_master handle, error = %lu", - GetLastError ()); - error_code (EACCES); - } - - if (to_master) - if (check_and_dup_handle (from_process_handle, to_process_handle, - token_handle, - GENERIC_WRITE, - to_master, - &req.to_master, TRUE) != 0) - { - system_printf ("error duplicating to_master handle, error = %lu", - GetLastError ()); - error_code (EACCES); - } - - CloseHandle (from_process_handle); - CloseHandle (to_process_handle); - CloseHandle (token_handle); - - debug_printf ("%lu(%lu, %lu) -> %lu(%lu,%lu)", - req.master_pid, from_master, to_master, - req.pid, req.from_master, req.to_master); - - return; -} - -void -client_request_get_version::serve (transport_layer_base *, process_cache *) -{ - assert (!error_code ()); - - if (msglen ()) - syscall_printf ("unexpected request body ignored: %lu bytes", msglen ()); - - msglen (sizeof (version)); - - version.major = CYGWIN_SERVER_VERSION_MAJOR; - version.api = CYGWIN_SERVER_VERSION_API; - version.minor = CYGWIN_SERVER_VERSION_MINOR; - version.patch = CYGWIN_SERVER_VERSION_PATCH; -} - -class server_request : public queue_request -{ -public: - server_request (transport_layer_base *const conn, process_cache *const cache) - : _conn (conn), _cache (cache) - {} - - virtual ~server_request () - { - safe_delete (_conn); - } - - virtual void process () - { - client_request::handle_request (_conn, _cache); - } - -private: - transport_layer_base *const _conn; - process_cache *const _cache; -}; - -class server_submission_loop : public queue_submission_loop -{ -public: - server_submission_loop (threaded_queue *const queue, - transport_layer_base *const transport, - process_cache *const cache) - : queue_submission_loop (queue, false), - _transport (transport), - _cache (cache) - { - assert (_transport); - assert (_cache); - } - -private: - transport_layer_base *const _transport; - process_cache *const _cache; - - virtual void request_loop (); -}; - -/* FIXME: this is a little ugly. What we really want is to wait on - * two objects: one for the pipe/socket, and one for being told to - * shutdown. Otherwise this will stay a problem (we won't actually - * shutdown until the request _AFTER_ the shutdown request. And - * sending ourselves a request is ugly - */ -void -server_submission_loop::request_loop () -{ - /* I'd like the accepting thread's priority to be above any "normal" - * thread in the system to avoid overflowing the listen queue (for - * sockets; similar issues exist for named pipes); but, for example, - * a normal priority thread in a foregrounded process is boosted to - * THREAD_PRIORITY_HIGHEST (AFAICT). Thus try to set the current - * thread's priority to a level one above that. This fails on - * win9x/ME so assume any failure in that call is due to that and - * simply call again at one priority level lower. - */ - if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST + 1)) - if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST)) - debug_printf ("failed to raise accept thread priority, error = %lu", - GetLastError ()); - - while (_running) - { - bool recoverable = false; - transport_layer_base *const conn = _transport->accept (&recoverable); - if (!conn && !recoverable) - { - system_printf ("fatal error on IPC transport: closing down"); - return; - } - // EINTR probably implies a shutdown request; so back off for a - // moment to let the main thread take control, otherwise the - // server spins here receiving EINTR repeatedly since the signal - // handler in the main thread doesn't get a chance to be called. - if (!conn && errno == EINTR) - { - if (!SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL)) - debug_printf ("failed to reset thread priority, error = %lu", - GetLastError ()); - - Sleep (0); - if (!SetThreadPriority (GetCurrentThread (), - THREAD_PRIORITY_HIGHEST + 1)) - if (!SetThreadPriority (GetCurrentThread (), - THREAD_PRIORITY_HIGHEST)) - debug_printf ("failed to raise thread priority, error = %lu", - GetLastError ()); - } - if (conn) - _queue->add (safe_new (server_request, conn, _cache)); - } -} - -client_request_shutdown::client_request_shutdown () - : client_request (CYGSERVER_REQUEST_SHUTDOWN) -{ - // verbose: syscall_printf ("created"); -} - -void -client_request_shutdown::serve (transport_layer_base *, process_cache *) -{ - assert (!error_code ()); - - if (msglen ()) - syscall_printf ("unexpected request body ignored: %lu bytes", msglen ()); - - /* FIXME: link upwards, and then this becomes a trivial method call to - * only shutdown _this queue_ - */ - - kill (getpid (), SIGINT); - - msglen (0); -} - -static sig_atomic_t shutdown_server = false; - -static void -handle_signal (const int signum) -{ - /* any signal makes us die :} */ - - shutdown_server = true; -} - -/* - * print_usage () - */ - -static void -print_usage (const char *const pgm) -{ - printf ("Usage: %s [OPTIONS]\n", pgm); - printf (" -c, --cleanup-threads number of cleanup threads to use\n"); - printf (" -h, --help output usage information and exit\n"); - printf (" -r, --request-threads number of request threads to use\n"); - printf (" -s, --shutdown shutdown the daemon\n"); - printf (" -v, --version output version information and exit\n"); -} - -/* - * print_version () - */ - -static void -print_version (const char *const pgm) -{ - char *vn = NULL; - - const char *const colon = strchr (version, ':'); - - if (!colon) - { - vn = strdup ("?"); - } - else - { - vn = strdup (colon + 2); // Skip ": " - - char *const spc = strchr (vn, ' '); - - if (spc) - *spc = '\0'; - } - - char buf[200]; - snprintf (buf, sizeof (buf), "%d.%d.%d(%d.%d/%d/%d)-(%d.%d.%d.%d) %s", - cygwin_version.dll_major / 1000, - cygwin_version.dll_major % 1000, - cygwin_version.dll_minor, - cygwin_version.api_major, - cygwin_version.api_minor, - cygwin_version.shared_data, - CYGWIN_SERVER_VERSION_MAJOR, - CYGWIN_SERVER_VERSION_API, - CYGWIN_SERVER_VERSION_MINOR, - CYGWIN_SERVER_VERSION_PATCH, - cygwin_version.mount_registry, - cygwin_version.dll_build_date); - - printf ("%s (cygwin) %s\n", pgm, vn); - printf ("API version %s\n", buf); - printf ("Copyright 2001, 2002 Red Hat, Inc.\n"); - printf ("Compiled on %s\n", __DATE__); - - free (vn); -} - -/* - * main () - */ - -int -main (const int argc, char *argv[]) -{ - const struct option longopts[] = { - {"cleanup-threads", required_argument, NULL, 'c'}, - {"help", no_argument, NULL, 'h'}, - {"request-threads", required_argument, NULL, 'r'}, - {"shutdown", no_argument, NULL, 's'}, - {"version", no_argument, NULL, 'v'}, - {0, no_argument, NULL, 0} - }; - - const char opts[] = "c:hr:sv"; - - int cleanup_threads = 2; - int request_threads = 10; - bool shutdown = false; - - const char *pgm = NULL; - - if (!(pgm = strrchr (*argv, '\\')) && !(pgm = strrchr (*argv, '/'))) - pgm = *argv; - else - pgm++; - - wincap.init (); - if (wincap.has_security ()) - setup_privileges (); - - int opt; - - while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) - switch (opt) - { - case 'c': - cleanup_threads = atoi (optarg); - if (cleanup_threads <= 0) - { - fprintf (stderr, - "%s: number of cleanup threads must be positive\n", - pgm); - exit (1); - } - break; - - case 'h': - print_usage (pgm); - return 0; - - case 'r': - request_threads = atoi (optarg); - if (request_threads <= 0) - { - fprintf (stderr, - "%s: number of request threads must be positive\n", - pgm); - exit (1); - } - break; - - case 's': - shutdown = true; - break; - - case 'v': - print_version (pgm); - return 0; - - case '?': - fprintf (stderr, "Try `%s --help' for more information.\n", pgm); - exit (1); - } - - if (optind != argc) - { - fprintf (stderr, "%s: too many arguments\n", pgm); - exit (1); - } - - if (shutdown) - { - /* Setting `cygserver_running' stops the request code making a - * version request, which is not much to the point. - */ - cygserver_running = CYGSERVER_OK; - - client_request_shutdown req; - - if (req.make_request () == -1 || req.error_code ()) - { - fprintf (stderr, "%s: shutdown request failed: %s\n", - pgm, strerror (req.error_code ())); - exit (1); - } - - // FIXME: It would be nice to wait here for the daemon to exit. - - return 0; - } - -#define SIGHANDLE(SIG) \ - do \ - { \ - struct sigaction act; \ - \ - act.sa_handler = &handle_signal; \ - act.sa_mask = 0; \ - act.sa_flags = 0; \ - \ - if (sigaction (SIG, &act, NULL) == -1) \ - { \ - system_printf ("failed to install handler for " #SIG ": %s", \ - strerror (errno)); \ - exit (1); \ - } \ - } while (false) - - SIGHANDLE (SIGHUP); - SIGHANDLE (SIGINT); - SIGHANDLE (SIGTERM); - - print_version (pgm); - setbuf (stdout, NULL); - printf ("daemon starting up"); - - threaded_queue request_queue (request_threads); - printf ("."); - - transport_layer_base *const transport = create_server_transport (); - assert (transport); - printf ("."); - - process_cache cache (cleanup_threads); - printf ("."); - - server_submission_loop submission_loop (&request_queue, transport, &cache); - printf ("."); - - request_queue.add_submission_loop (&submission_loop); - printf ("."); - - if (transport->listen () == -1) - { - exit (1); - } - printf ("."); - - cache.start (); - printf ("."); - - request_queue.start (); - printf ("."); - - printf ("complete\n"); - - /* TODO: wait on multiple objects - the thread handle for each - * request loop + all the process handles. This should be done by - * querying the request_queue and the process cache for all their - * handles, and then waiting for (say) 30 seconds. after that we - * recreate the list of handles to wait on, and wait again. the - * point of all this abstraction is that we can trivially server - * both sockets and pipes simply by making a new transport, and then - * calling request_queue.process_requests (transport2); - */ - /* WaitForMultipleObjects abort && request_queue && process_queue && signal - -- if signal event then retrigger it - */ - while (!shutdown_server && request_queue.running () && cache.running ()) - pause (); - - printf ("\nShutdown request received - new requests will be denied\n"); - request_queue.stop (); - printf ("All pending requests processed\n"); - safe_delete (transport); - printf ("No longer accepting requests - cygwin will operate in daemonless mode\n"); - cache.stop (); - printf ("All outstanding process-cache activities completed\n"); - printf ("daemon shutdown\n"); - - return 0; -} diff --git a/winsup/cygwin/cygserver_client.cc b/winsup/cygwin/cygserver_client.cc deleted file mode 100755 index f6683182d72..00000000000 --- a/winsup/cygwin/cygserver_client.cc +++ /dev/null @@ -1,528 +0,0 @@ -/* cygserver_client.cc - - Copyright 2001, 2002 Red Hat Inc. - - Written by Egor Duda <deo@logos-m.ru> - -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. */ - -/* to allow this to link into cygwin and the .dll, a little magic is needed. */ -#ifdef __OUTSIDE_CYGWIN__ -#include "woutsup.h" -#else -#include "winsup.h" -#endif - -#include <assert.h> -#include <stdio.h> -#include <unistd.h> - -#include "cygerrno.h" -#include "cygserver_shm.h" -#include "safe_memory.h" - -#include "cygwin/cygserver.h" -#include "cygwin/cygserver_transport.h" - -int cygserver_running = CYGSERVER_UNKNOWN; // Nb: inherited by children. - -/* On by default during development. For release, we probably want off - * by default. - */ -bool allow_daemon = true; // Nb: inherited by children. - -client_request_get_version::client_request_get_version () - : client_request (CYGSERVER_REQUEST_GET_VERSION, &version, sizeof (version)) -{ - msglen (0); // No parameters for request. - - // verbose: syscall_printf ("created"); -} - -/* - * client_request_get_version::check_version () - * - * The major version and API version numbers must match exactly. An - * older than expected minor version number is accepted (as long as - * the first numbers match, that is). - */ - -bool -client_request_get_version::check_version () const -{ - const bool ok = (version.major == CYGWIN_SERVER_VERSION_MAJOR - && version.api == CYGWIN_SERVER_VERSION_API - && version.minor <= CYGWIN_SERVER_VERSION_MINOR); - - if (!ok) - syscall_printf (("incompatible version of cygwin server: " - "client version %d.%d.%d.%d, " - "server version %ld.%ld.%ld.%ld"), - CYGWIN_SERVER_VERSION_MAJOR, - CYGWIN_SERVER_VERSION_API, - CYGWIN_SERVER_VERSION_MINOR, - CYGWIN_SERVER_VERSION_PATCH, - version.major, - version.api, - version.minor, - version.patch); - - return ok; -} - -#ifdef __INSIDE_CYGWIN__ - -client_request_attach_tty::client_request_attach_tty (DWORD nmaster_pid, - HANDLE nfrom_master, - HANDLE nto_master) - : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req)) -{ - req.pid = GetCurrentProcessId (); - req.master_pid = nmaster_pid; - req.from_master = nfrom_master; - req.to_master = nto_master; - - syscall_printf (("created: pid = %lu, master_pid = %lu, " - "from_master = %lu, to_master = %lu"), - req.pid, req.master_pid, req.from_master, req.to_master); -} - -#else /* !__INSIDE_CYGWIN__ */ - -client_request_attach_tty::client_request_attach_tty () - : client_request (CYGSERVER_REQUEST_ATTACH_TTY, &req, sizeof (req)) -{ - // verbose: syscall_printf ("created"); -} - -#endif /* __INSIDE_CYGWIN__ */ - -/* - * client_request_attach_tty::send () - * - * Wraps the base method to provide error handling support. If the - * reply contains a body but is flagged as an error, close any handles - * that have been returned by cygserver and then discard the message - * body, i.e. the client either sees a successful result with handles - * or an unsuccessful result with no handles. - */ - -void -client_request_attach_tty::send (transport_layer_base * const conn) -{ - client_request::send (conn); - - if (msglen () && error_code ()) - { - if (from_master ()) - CloseHandle (from_master ()); - if (to_master ()) - CloseHandle (to_master ()); - msglen (0); - } -} - -client_request::header_t::header_t (const request_code_t request_code, - const size_t msglen) - : msglen (msglen), - request_code (request_code) -{ - assert (request_code >= 0 && request_code < CYGSERVER_REQUEST_LAST); -} - -// FIXME: also check write and read result for -1. - -void -client_request::send (transport_layer_base * const conn) -{ - assert (conn); - assert (!(msglen () && !_buf)); // i.e., msglen () implies _buf - assert (msglen () <= _buflen); - - { - const ssize_t count = conn->write (&_header, sizeof (_header)); - - if (count != sizeof (_header)) - { - assert (errno); - error_code (errno); - syscall_printf (("request header write failure: " - "only %ld bytes sent of %ld, " - "error = %d(%lu)"), - count, sizeof (_header), - errno, GetLastError ()); - return; - } - } - - if (msglen ()) - { - const ssize_t count = conn->write (_buf, msglen ()); - - if (count == -1 || (size_t) count != msglen ()) - { - assert (errno); - error_code (errno); - syscall_printf (("request body write failure: " - "only %ld bytes sent of %ld, " - "error = %d(%lu)"), - count, msglen (), - errno, GetLastError ()); - return; - } - } - - // verbose: syscall_printf ("request sent (%ld + %ld bytes)", - // sizeof (_header), msglen ()); - - { - const ssize_t count = conn->read (&_header, sizeof (_header)); - - if (count != sizeof (_header)) - { - assert (errno); - error_code (errno); - syscall_printf (("reply header read failure: " - "only %ld bytes received of %ld, " - "error = %d(%lu)"), - count, sizeof (_header), - errno, GetLastError ()); - return; - } - } - - if (msglen () && !_buf) - { - system_printf ("no client buffer for reply body: %ld bytes needed", - msglen ()); - error_code (EINVAL); - return; - } - - if (msglen () > _buflen) - { - system_printf (("client buffer too small for reply body: " - "have %ld bytes and need %ld"), - _buflen, msglen ()); - error_code (EINVAL); - return; - } - - if (msglen ()) - { - const ssize_t count = conn->read (_buf, msglen ()); - - if (count == -1 || (size_t) count != msglen ()) - { - assert (errno); - error_code (errno); - syscall_printf (("reply body read failure: " - "only %ld bytes received of %ld, " - "error = %d(%lu)"), - count, msglen (), - errno, GetLastError ()); - return; - } - } - - // verbose: syscall_printf ("reply received (%ld + %ld bytes)", - // sizeof (_header), msglen ()); -} - -#ifndef __INSIDE_CYGWIN__ - -/* - * client_request::handle_request () - * - * A server-side method. - * - * This is a factory method for the client_request subclasses. It - * reads the incoming request header and, based on its request code, - * creates an instance of the appropriate class. - * - * FIXME: If the incoming packet is malformed, the server drops it on - * the floor. Should it try and generate some sort of reply for the - * client? As it is, the client will simply get a broken connection. - * - * FIXME: also check write and read result for -1. - */ - -/* static */ void -client_request::handle_request (transport_layer_base *const conn, - process_cache *const cache) -{ - // verbose: debug_printf ("about to read"); - - header_t header; - - { - const ssize_t count = conn->read (&header, sizeof (header)); - - if (count != sizeof (header)) - { - syscall_printf (("request header read failure: " - "only %ld bytes received of %ld, " - "error = %d(%lu)"), - count, sizeof (header), - errno, GetLastError ()); - return; - } - - // verbose: debug_printf ("got header (%ld)", count); - } - - client_request *req = NULL; - - switch (header.request_code) - { - case CYGSERVER_REQUEST_GET_VERSION: - req = safe_new0 (client_request_get_version); - break; - case CYGSERVER_REQUEST_SHUTDOWN: - req = safe_new0 (client_request_shutdown); - break; - case CYGSERVER_REQUEST_ATTACH_TTY: - req = safe_new0 (client_request_attach_tty); - break; - case CYGSERVER_REQUEST_SHM: - req = safe_new0 (client_request_shm); - break; - default: - syscall_printf ("unknown request code %d received: request ignored", - header.request_code); - return; - } - - assert (req); - - req->msglen (header.msglen); - req->handle (conn, cache); - - safe_delete (req); - -#ifndef DEBUGGING - printf ("."); // A little noise when we're being quiet. -#endif -} - -#endif /* !__INSIDE_CYGWIN__ */ - -client_request::client_request (request_code_t const id, - void * const buf, - size_t const buflen) - : _header (id, buflen), - _buf (buf), - _buflen (buflen) -{ - assert ((!_buf && !_buflen) || (_buf && _buflen)); -} - -client_request::~client_request () -{} - -int -client_request::make_request () -{ - assert (cygserver_running == CYGSERVER_UNKNOWN \ - || cygserver_running == CYGSERVER_OK \ - || cygserver_running == CYGSERVER_UNAVAIL); - - if (cygserver_running == CYGSERVER_UNKNOWN) - cygserver_init (); - - assert (cygserver_running == CYGSERVER_OK \ - || cygserver_running == CYGSERVER_UNAVAIL); - - /* Don't retry every request if the server's not there */ - if (cygserver_running == CYGSERVER_UNAVAIL) - { - syscall_printf ("cygserver un-available"); - error_code (ENOSYS); - return -1; - } - - transport_layer_base *const transport = create_server_transport (); - - assert (transport); - - if (transport->connect () == -1) - { - if (errno) - error_code (errno); - else - error_code (ENOSYS); - safe_delete (transport); - return -1; - } - - // verbose: debug_printf ("connected to server %p", transport); - - send (transport); - - safe_delete (transport); - - return 0; -} - -#ifndef __INSIDE_CYGWIN__ - -/* - * client_request::handle () - * - * A server-side method. - * - * At this point, the header of an incoming request has been read and - * an appropriate client_request object constructed. This method has - * to read the request body into its buffer, if there is such a body, - * then perform the request and send back the results to the client. - * - * FIXME: If the incoming packet is malformed, the server drops it on - * the floor. Should it try and generate some sort of reply for the - * client? As it is, the client will simply get a broken connection. - * - * FIXME: also check write and read result for -1. - */ - -void -client_request::handle (transport_layer_base *const conn, - process_cache *const cache) -{ - if (msglen () && !_buf) - { - system_printf ("no buffer for request body: %ld bytes needed", - msglen ()); - error_code (EINVAL); - return; - } - - if (msglen () > _buflen) - { - system_printf (("buffer too small for request body: " - "have %ld bytes and need %ld"), - _buflen, msglen ()); - error_code (EINVAL); - return; - } - - if (msglen ()) - { - const ssize_t count = conn->read (_buf, msglen ()); - - if (count == -1 || (size_t) count != msglen ()) - { - assert (errno); - error_code (errno); - syscall_printf (("request body read failure: " - "only %ld bytes received of %ld, " - "error = %d(%lu)"), - count, msglen (), - errno, GetLastError ()); - return; - } - } - - // verbose: syscall_printf ("request received (%ld + %ld bytes)", - // sizeof (_header), msglen ()); - - error_code (0); // Overwrites the _header.request_code field. - - /* - * This is not allowed to fail. We must return ENOSYS at a minimum - * to the client. - */ - serve (conn, cache); - - { - const ssize_t count = conn->write (&_header, sizeof (_header)); - - if (count != sizeof (_header)) - { - assert (errno); - error_code (errno); - syscall_printf (("reply header write failure: " - "only %ld bytes sent of %ld, " - "error = %d(%lu)"), - count, sizeof (_header), - errno, GetLastError ()); - return; - } - } - - if (msglen ()) - { - const ssize_t count = conn->write (_buf, msglen ()); - - if (count == -1 || (size_t) count != msglen ()) - { - assert (errno); - error_code (errno); - syscall_printf (("reply body write failure: " - "only %ld bytes sent of %ld, " - "error = %d(%lu)"), - count, msglen (), - errno, GetLastError ()); - return; - } - } - - // verbose: syscall_printf ("reply sent (%ld + %ld bytes)", - // sizeof (_header), msglen ()); -} - -#endif /* !__INSIDE_CYGWIN__ */ - -bool -check_cygserver_available () -{ - assert (cygserver_running == CYGSERVER_UNKNOWN \ - || cygserver_running == CYGSERVER_UNAVAIL); - - cygserver_running = CYGSERVER_OK; // For make_request (). - - client_request_get_version req; - - /* This indicates that we failed to connect to cygserver at all but - * that's fine as cygwin doesn't need it to be running. - */ - if (req.make_request () == -1) - return false; - - /* We connected to the server but something went wrong after that - * (in sending the message, in cygserver itself, or in receiving the - * reply). - */ - if (req.error_code ()) - { - syscall_printf ("failure in cygserver version request: %d", - req.error_code ()); - syscall_printf ("process will continue without cygserver support"); - return false; - } - - return req.check_version (); -} - -void -cygserver_init () -{ - if (!allow_daemon) - { - syscall_printf ("cygserver use disabled in client"); - cygserver_running = CYGSERVER_UNAVAIL; - return; - } - - assert (cygserver_running == CYGSERVER_UNKNOWN \ - || cygserver_running == CYGSERVER_OK \ - || cygserver_running == CYGSERVER_UNAVAIL); - - if (cygserver_running == CYGSERVER_OK) - return; - - if (!check_cygserver_available ()) - cygserver_running = CYGSERVER_UNAVAIL; -} diff --git a/winsup/cygwin/cygserver_process.cc b/winsup/cygwin/cygserver_process.cc deleted file mode 100755 index 2cc7be19c92..00000000000 --- a/winsup/cygwin/cygserver_process.cc +++ /dev/null @@ -1,431 +0,0 @@ -/* cygserver_process.cc - - Copyright 2001, 2002 Red Hat Inc. - - Written 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. */ - -#include "woutsup.h" - -#include <sys/types.h> - -#include <assert.h> -#include <stdlib.h> - -#include "cygerrno.h" - -#include "cygwin/cygserver_process.h" - -/*****************************************************************************/ - -#define elements(ARRAY) (sizeof (ARRAY) / sizeof (*ARRAY)) - -/*****************************************************************************/ - -process_cleanup::~process_cleanup () -{ - safe_delete (_process); -} - -void -process_cleanup::process () -{ - _process->cleanup (); -} - -/*****************************************************************************/ - -/* cleanup_routine */ -cleanup_routine::~cleanup_routine () -{ -} - -/*****************************************************************************/ - -process::process (const pid_t cygpid, const DWORD winpid) - : _cygpid (cygpid), - _winpid (winpid), - _hProcess (NULL), - _cleaning_up (false), - _exit_status (STILL_ACTIVE), - _routines_head (NULL), - _next (NULL) -{ - _hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, winpid); - if (!_hProcess) - { - system_printf ("unable to obtain handle for new cache process %d(%lu)", - _cygpid, _winpid); - _hProcess = INVALID_HANDLE_VALUE; - _exit_status = 0; - } - else - debug_printf ("got handle %p for new cache process %d(%lu)", - _hProcess, _cygpid, _winpid); - InitializeCriticalSection (&_access); -} - -process::~process () -{ - DeleteCriticalSection (&_access); - (void) CloseHandle (_hProcess); -} - -/* No need to be thread-safe as this is only ever called by - * process_cache::remove_process (). If it has to be made thread-safe - * later on, it should not use the `access' critical section as that - * is held by the client request handlers for an arbitrary length of - * time, i.e. while they do whatever processing is required for a - * client request. - */ -DWORD -process::check_exit_code () -{ - if (_hProcess && _hProcess != INVALID_HANDLE_VALUE - && _exit_status == STILL_ACTIVE - && !GetExitCodeProcess (_hProcess, &_exit_status)) - { - system_printf ("failed to retrieve exit code for %d(%lu), error = %lu", - _cygpid, _winpid, GetLastError ()); - _hProcess = INVALID_HANDLE_VALUE; - } - return _exit_status; -} - -bool -process::add (cleanup_routine *const entry) -{ - assert (entry); - - bool res = false; - EnterCriticalSection (&_access); - - if (!_cleaning_up) - { - entry->_next = _routines_head; - _routines_head = entry; - res = true; - } - - LeaveCriticalSection (&_access); - return res; -} - -bool -process::remove (const cleanup_routine *const entry) -{ - assert (entry); - - bool res = false; - EnterCriticalSection (&_access); - - if (!_cleaning_up) - { - cleanup_routine *previous = NULL; - - for (cleanup_routine *ptr = _routines_head; - ptr; - previous = ptr, ptr = ptr->_next) - { - if (*ptr == *entry) - { - if (previous) - previous->_next = ptr->_next; - else - _routines_head = ptr->_next; - - safe_delete (ptr); - res = true; - break; - } - } - } - - LeaveCriticalSection (&_access); - return res; -} - -/* This is single threaded. It's called after the process is removed - * from the cache, but inserts may be attemped by worker threads that - * have a pointer to it. - */ -void -process::cleanup () -{ - EnterCriticalSection (&_access); - assert (!is_active ()); - assert (!_cleaning_up); - InterlockedExchange (&_cleaning_up, true); - cleanup_routine *entry = _routines_head; - _routines_head = NULL; - LeaveCriticalSection (&_access); - - while (entry) - { - cleanup_routine *const ptr = entry; - entry = entry->_next; - ptr->cleanup (this); - safe_delete (ptr); - } -} - -/*****************************************************************************/ - -void -process_cache::submission_loop::request_loop () -{ - assert (this); - assert (_cache); - assert (_interrupt_event); - - while (_running) - _cache->wait_for_processes (_interrupt_event); -} - -/*****************************************************************************/ - -process_cache::process_cache (const unsigned int initial_workers) - : _queue (initial_workers), - _submitter (this, &_queue), // true == interruptible - _processes_count (0), - _processes_head (NULL), - _cache_add_trigger (NULL) -{ - /* there can only be one */ - InitializeCriticalSection (&_cache_write_access); - - _cache_add_trigger = CreateEvent (NULL, // SECURITY_ATTRIBUTES - FALSE, // Auto-reset - FALSE, // Initially non-signalled - NULL); // Anonymous - - if (!_cache_add_trigger) - { - system_printf ("failed to create cache add trigger, error = %lu", - GetLastError ()); - abort (); - } - - _queue.add_submission_loop (&_submitter); -} - -process_cache::~process_cache () -{ - (void) CloseHandle (_cache_add_trigger); - DeleteCriticalSection (&_cache_write_access); -} - -/* This returns the process object to the caller already locked, that - * is, with the object's `access' critical region entered. Thus the - * caller must unlock the object when it's finished with it (via - * process::release ()). It must then not try to access the object - * afterwards, except by going through this routine again, as it may - * have been deleted once it has been unlocked. - */ -class process * -process_cache::process (const pid_t cygpid, const DWORD winpid) -{ - /* TODO: make this more granular, so a search doesn't involve the - * write lock. - */ - EnterCriticalSection (&_cache_write_access); - class process *previous = NULL; - class process *entry = find (winpid, &previous); - - if (!entry) - { - if (_processes_count + SPECIALS_COUNT >= MAXIMUM_WAIT_OBJECTS) - { - LeaveCriticalSection (&_cache_write_access); - system_printf (("process limit (%d processes) reached; " - "new connection refused for %d(%lu)"), - MAXIMUM_WAIT_OBJECTS - SPECIALS_COUNT, - cygpid, winpid); - set_errno (EAGAIN); - return NULL; - } - - entry = safe_new (class process, cygpid, winpid); - if (!entry->is_active ()) - { - LeaveCriticalSection (&_cache_write_access); - safe_delete (entry); - set_errno (ESRCH); - return NULL; - } - - if (previous) - { - entry->_next = previous->_next; - previous->_next = entry; - } - else - { - entry->_next = _processes_head; - _processes_head = entry; - } - - _processes_count += 1; - SetEvent (_cache_add_trigger); - } - - EnterCriticalSection (&entry->_access); // To be released by the caller. - LeaveCriticalSection (&_cache_write_access); - assert (entry); - assert (entry->_winpid == winpid); - return entry; -} - -void -process_cache::wait_for_processes (const HANDLE interrupt_event) -{ - // Update `_wait_array' with handles of all current processes. - const size_t count = sync_wait_array (interrupt_event); - - debug_printf ("waiting on %u objects in total (%u processes)", - count, _processes_count); - - const DWORD rc = WaitForMultipleObjects (count, _wait_array, - FALSE, INFINITE); - - if (rc == WAIT_FAILED) - { - system_printf ("could not wait on the process handles, error = %lu", - GetLastError ()); - abort (); - } - - const size_t start = rc - WAIT_OBJECT_0; - - if (rc < WAIT_OBJECT_0 || start > count) - { - system_printf (("unexpected return code %rc " - "from WaitForMultipleObjects: " - "expected [%u .. %u)"), - rc, WAIT_OBJECT_0, WAIT_OBJECT_0 + count); - abort (); - } - - // Tell all the processes, from the signalled point up, the bad news. - for (size_t index = start; index != count; index++) - if (_process_array[index]) - check_and_remove_process (index); -} - -/* - * process_cache::sync_wait_array () - * - * Fill-in the wait array with the handles that the cache needs to wait on. - * These handles are: - * - the process_process_param's interrupt event - * - the process_cache's cache_add_trigger event - * - the handle for each live process in the cache. - * - * Return value: the number of live handles in the array. - */ - -size_t -process_cache::sync_wait_array (const HANDLE interrupt_event) -{ - assert (this); - assert (_cache_add_trigger && _cache_add_trigger != INVALID_HANDLE_VALUE); - assert (interrupt_event && interrupt_event != INVALID_HANDLE_VALUE); - - EnterCriticalSection (&_cache_write_access); - - assert (_processes_count + SPECIALS_COUNT <= elements (_wait_array)); - - size_t index = 0; - - for (class process *ptr = _processes_head; ptr; ptr = ptr->_next) - { - assert (ptr->_hProcess && ptr->_hProcess != INVALID_HANDLE_VALUE); - assert (ptr->is_active ()); - - _wait_array[index] = ptr->handle (); - _process_array[index++] = ptr; - - assert (index <= elements (_wait_array)); - } - - /* Sorry for shouting, but THESE MUST BE ADDED AT THE END! */ - /* Well, not strictly `must', but it's more efficient if they are :-) */ - - _wait_array[index] = interrupt_event; - _process_array[index++] = NULL; - - _wait_array[index] = _cache_add_trigger; - _process_array[index++] = NULL; - - /* Phew, back to normal volume now. */ - - assert (index <= elements (_wait_array)); - - LeaveCriticalSection (&_cache_write_access); - - return index; -} - -void -process_cache::check_and_remove_process (const size_t index) -{ - assert (this); - assert (index < elements (_wait_array) - SPECIALS_COUNT); - - class process *const process = _process_array[index]; - - assert (process); - assert (process->handle () == _wait_array[index]); - - if (process->check_exit_code () == STILL_ACTIVE) - return; - - debug_printf ("process %d(%lu) has left the building ($? = %lu)", - process->_cygpid, process->_winpid, process->_exit_status); - - /* Unlink the process object from the process list. */ - - EnterCriticalSection (&_cache_write_access); - - class process *previous = NULL; - - const class process *const tmp = find (process->_winpid, &previous); - - assert (tmp == process); - assert (previous ? previous->_next == process : _processes_head == process); - - if (previous) - previous->_next = process->_next; - else - _processes_head = process->_next; - - _processes_count -= 1; - LeaveCriticalSection (&_cache_write_access); - - /* Schedule any cleanup tasks for this process. */ - _queue.add (safe_new (process_cleanup, process)); -} - -class process * -process_cache::find (const DWORD winpid, class process **previous) -{ - if (previous) - *previous = NULL; - - for (class process *ptr = _processes_head; ptr; ptr = ptr->_next) - if (ptr->_winpid == winpid) - return ptr; - else if (ptr->_winpid > winpid) // The list is sorted by winpid. - return NULL; - else if (previous) - *previous = ptr; - - return NULL; -} - -/*****************************************************************************/ diff --git a/winsup/cygwin/cygserver_shm.h b/winsup/cygwin/cygserver_shm.h deleted file mode 100644 index 5a5ee38207e..00000000000 --- a/winsup/cygwin/cygserver_shm.h +++ /dev/null @@ -1,147 +0,0 @@ -/* cygserver_shm.h: Single unix specification IPC interface for Cygwin. - - Copyright 2002 Red Hat, Inc. - - Written by Conrad Scott <conrad.scott@dsl.pipex.com>. - Based on code by Robert Collins <robert.collins@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. */ - -#ifndef __CYGSERVER_SHM_H__ -#define __CYGSERVER_SHM_H__ - -#include <sys/types.h> -#include <cygwin/shm.h> - -#include <assert.h> -#include <limits.h> - -#include "cygserver_ipc.h" - -#include "cygwin/cygserver.h" - -/*---------------------------------------------------------------------------* - * Values for the shminfo entries. - * - * Nb. The values are segregated between two enums so that the `small' - * values aren't promoted to `unsigned long' equivalents. - *---------------------------------------------------------------------------*/ - -enum - { - SHMMAX = ULONG_MAX, - SHMSEG = ULONG_MAX, - SHMALL = ULONG_MAX - }; - -enum - { - SHMMIN = 1, - SHMMNI = IPCMNI // Must be <= IPCMNI. - }; - -/*---------------------------------------------------------------------------* - * class client_request_shm - *---------------------------------------------------------------------------*/ - -#ifndef __INSIDE_CYGWIN__ -class transport_layer_base; -class process_cache; -#endif - -class client_request_shm : public client_request -{ - friend class client_request; - -public: - enum shmop_t - { - SHMOP_shmat, - SHMOP_shmctl, - SHMOP_shmdt, - SHMOP_shmget - }; - -#ifdef __INSIDE_CYGWIN__ - client_request_shm (int shmid, int shmflg); // shmat - client_request_shm (int shmid, int cmd, const struct shmid_ds *); // shmctl - client_request_shm (int shmid); // shmdt - client_request_shm (key_t, size_t, int shmflg); // shmget -#endif - - // Accessors for out parameters. - - int shmid () const - { - assert (!error_code ()); - return _parameters.out.shmid; - } - - HANDLE hFileMap () const - { - assert (!error_code ()); - return _parameters.out.hFileMap; - } - - const struct shmid_ds & ds () const - { - assert (!error_code ()); - return _parameters.out.ds; - } - - const struct shminfo & shminfo () const - { - assert (!error_code ()); - return _parameters.out.shminfo; - } - - const struct shm_info & shm_info () const - { - assert (!error_code ()); - return _parameters.out.shm_info; - } - -private: - union - { - struct - { - shmop_t shmop; - key_t key; - size_t size; - int shmflg; - int shmid; - int cmd; - pid_t cygpid; - DWORD winpid; - __uid32_t uid; - __gid32_t gid; - struct shmid_ds ds; - } in; - - struct { - int shmid; - union - { - HANDLE hFileMap; - struct shmid_ds ds; - struct shminfo shminfo; - struct shm_info shm_info; - }; - } out; - } _parameters; - -#ifndef __INSIDE_CYGWIN__ - client_request_shm (); -#endif - -#ifndef __INSIDE_CYGWIN__ - virtual void serve (transport_layer_base *, process_cache *); -#endif -}; - -#endif /* __CYGSERVER_SHM_H__ */ diff --git a/winsup/cygwin/cygserver_transport_pipes.cc b/winsup/cygwin/cygserver_transport_pipes.cc deleted file mode 100755 index 6d80defd4ed..00000000000 --- a/winsup/cygwin/cygserver_transport_pipes.cc +++ /dev/null @@ -1,362 +0,0 @@ -/* cygserver_transport_pipes.cc - - Copyright 2001, 2002 Red Hat Inc. - - Written 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. */ - -/* to allow this to link into cygwin and the .dll, a little magic is needed. */ -#ifdef __OUTSIDE_CYGWIN__ -#include "woutsup.h" -#else -#include "winsup.h" -#endif - -#include <sys/types.h> - -#include <assert.h> -#include <netdb.h> -#include <pthread.h> -#include <unistd.h> - -#include "cygerrno.h" -#include "cygwin/cygserver_transport.h" -#include "cygwin/cygserver_transport_pipes.h" - -#ifndef __INSIDE_CYGWIN__ -#include "cygwin/cygserver.h" -#endif - -enum - { - MAX_WAIT_NAMED_PIPE_RETRY = 64, - WAIT_NAMED_PIPE_TIMEOUT = 10 // milliseconds - }; - -#ifndef __INSIDE_CYGWIN__ - -static pthread_once_t pipe_instance_lock_once = PTHREAD_ONCE_INIT; -static CRITICAL_SECTION pipe_instance_lock; -static long pipe_instance = 0; - -static void -initialise_pipe_instance_lock () -{ - assert (pipe_instance == 0); - InitializeCriticalSection (&pipe_instance_lock); -} - -#endif /* !__INSIDE_CYGWIN__ */ - -#ifndef __INSIDE_CYGWIN__ - -transport_layer_pipes::transport_layer_pipes (const HANDLE hPipe) - : _pipe_name (""), - _hPipe (hPipe), - _is_accepted_endpoint (true), - _is_listening_endpoint (false) -{ - assert (_hPipe); - assert (_hPipe != INVALID_HANDLE_VALUE); - - init_security (); -} - -#endif /* !__INSIDE_CYGWIN__ */ - -transport_layer_pipes::transport_layer_pipes () - : _pipe_name ("\\\\.\\pipe\\cygwin_lpc"), - _hPipe (NULL), - _is_accepted_endpoint (false), - _is_listening_endpoint (false) -{ - init_security (); -} - -void -transport_layer_pipes::init_security () -{ - assert (wincap.has_security ()); - - /* FIXME: pthread_once or equivalent needed */ - - InitializeSecurityDescriptor (&_sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl (&_sd, TRUE, NULL, FALSE); - - _sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES); - _sec_all_nih.lpSecurityDescriptor = &_sd; - _sec_all_nih.bInheritHandle = FALSE; -} - -transport_layer_pipes::~transport_layer_pipes () -{ - close (); -} - -#ifndef __INSIDE_CYGWIN__ - -int -transport_layer_pipes::listen () -{ - assert (!_hPipe); - assert (!_is_accepted_endpoint); - assert (!_is_listening_endpoint); - - _is_listening_endpoint = true; - - /* no-op */ - return 0; -} - -class transport_layer_pipes * -transport_layer_pipes::accept (bool *const recoverable) -{ - assert (!_hPipe); - assert (!_is_accepted_endpoint); - assert (_is_listening_endpoint); - - pthread_once (&pipe_instance_lock_once, &initialise_pipe_instance_lock); - - EnterCriticalSection (&pipe_instance_lock); - - // Read: http://www.securityinternals.com/research/papers/namedpipe.php - // See also the Microsoft security bulletins MS00-053 and MS01-031. - - // FIXME: Remove FILE_CREATE_PIPE_INSTANCE. - - const bool first_instance = (pipe_instance == 0); - - const HANDLE accept_pipe = - CreateNamedPipe (_pipe_name, - (PIPE_ACCESS_DUPLEX - | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0)), - (PIPE_TYPE_BYTE | PIPE_WAIT), - PIPE_UNLIMITED_INSTANCES, - 0, 0, 1000, - &_sec_all_nih); - - const bool duplicate = (accept_pipe == INVALID_HANDLE_VALUE - && pipe_instance == 0 - && GetLastError () == ERROR_ACCESS_DENIED); - - if (accept_pipe != INVALID_HANDLE_VALUE) - InterlockedIncrement (&pipe_instance); - - LeaveCriticalSection (&pipe_instance_lock); - - if (duplicate) - { - *recoverable = false; - system_printf ("failed to create named pipe: " - "is the daemon already running?"); - return NULL; - } - - if (accept_pipe == INVALID_HANDLE_VALUE) - { - debug_printf ("error creating pipe (%lu).", GetLastError ()); - *recoverable = true; // FIXME: case analysis? - return NULL; - } - - assert (accept_pipe); - - if (!ConnectNamedPipe (accept_pipe, NULL) - && GetLastError () != ERROR_PIPE_CONNECTED) - { - debug_printf ("error connecting to pipe (%lu)", GetLastError ()); - (void) CloseHandle (accept_pipe); - *recoverable = true; // FIXME: case analysis? - return NULL; - } - - return safe_new (transport_layer_pipes, accept_pipe); -} - -#endif /* !__INSIDE_CYGWIN__ */ - -void -transport_layer_pipes::close () -{ - // verbose: debug_printf ("closing pipe %p", _hPipe); - - if (_hPipe) - { - assert (_hPipe != INVALID_HANDLE_VALUE); - -#ifndef __INSIDE_CYGWIN__ - - if (_is_accepted_endpoint) - { - (void) FlushFileBuffers (_hPipe); // Blocks until client reads. - (void) DisconnectNamedPipe (_hPipe); - EnterCriticalSection (&pipe_instance_lock); - (void) CloseHandle (_hPipe); - assert (pipe_instance > 0); - InterlockedDecrement (&pipe_instance); - LeaveCriticalSection (&pipe_instance_lock); - } - else - (void) CloseHandle (_hPipe); - -#else /* __INSIDE_CYGWIN__ */ - - assert (!_is_accepted_endpoint); - (void) ForceCloseHandle (_hPipe); - -#endif /* __INSIDE_CYGWIN__ */ - - _hPipe = NULL; - } -} - -ssize_t -transport_layer_pipes::read (void *const buf, const size_t len) -{ - // verbose: debug_printf ("reading from pipe %p", _hPipe); - - assert (_hPipe); - assert (_hPipe != INVALID_HANDLE_VALUE); - assert (!_is_listening_endpoint); - - DWORD count; - if (!ReadFile (_hPipe, buf, len, &count, NULL)) - { - debug_printf ("error reading from pipe (%lu)", GetLastError ()); - set_errno (EINVAL); // FIXME? - return -1; - } - - return count; -} - -ssize_t -transport_layer_pipes::write (void *const buf, const size_t len) -{ - // verbose: debug_printf ("writing to pipe %p", _hPipe); - - assert (_hPipe); - assert (_hPipe != INVALID_HANDLE_VALUE); - assert (!_is_listening_endpoint); - - DWORD count; - if (!WriteFile (_hPipe, buf, len, &count, NULL)) - { - debug_printf ("error writing to pipe, error = %lu", GetLastError ()); - set_errno (EINVAL); // FIXME? - return -1; - } - - return count; -} - -/* - * This routine holds a static variable, assume_cygserver, that is set - * if the transport has good reason to think that cygserver is - * running, i.e. if if successfully connected to it with the previous - * attempt. If this is set, the code tries a lot harder to get a - * connection, making the assumption that any failures are just - * congestion and overloading problems. - */ - -int -transport_layer_pipes::connect () -{ - assert (!_hPipe); - assert (!_is_accepted_endpoint); - assert (!_is_listening_endpoint); - - static bool assume_cygserver = false; - - BOOL rc = TRUE; - int retries = 0; - - while (rc) - { - _hPipe = CreateFile (_pipe_name, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - &_sec_all_nih, - OPEN_EXISTING, - SECURITY_IMPERSONATION, - NULL); - - if (_hPipe != INVALID_HANDLE_VALUE) - { - assert (_hPipe); -#ifdef __INSIDE_CYGWIN__ - ProtectHandle (_hPipe); -#endif - assume_cygserver = true; - return 0; - } - - _hPipe = NULL; - - if (!assume_cygserver && GetLastError () != ERROR_PIPE_BUSY) - { - debug_printf ("Error opening the pipe (%lu)", GetLastError ()); - return -1; - } - - /* Note: `If no instances of the specified named pipe exist, the - * WaitNamedPipe function returns immediately, regardless of the - * time-out value.' Thus the explicit Sleep if the call fails - * with ERROR_FILE_NOT_FOUND. - */ - while (retries != MAX_WAIT_NAMED_PIPE_RETRY - && !(rc = WaitNamedPipe (_pipe_name, WAIT_NAMED_PIPE_TIMEOUT))) - { - if (GetLastError () == ERROR_FILE_NOT_FOUND) - Sleep (0); // Give the server a chance. - - retries += 1; - } - } - - assert (retries == MAX_WAIT_NAMED_PIPE_RETRY); - - system_printf ("lost connection to cygserver, error = %lu", - GetLastError ()); - - assume_cygserver = false; - - return -1; -} - -#ifndef __INSIDE_CYGWIN__ - -void -transport_layer_pipes::impersonate_client () -{ - assert (_hPipe); - assert (_hPipe != INVALID_HANDLE_VALUE); - assert (_is_accepted_endpoint); - - // verbose: debug_printf ("impersonating pipe %p", _hPipe); - if (_hPipe) - { - assert (_hPipe != INVALID_HANDLE_VALUE); - - if (!ImpersonateNamedPipeClient (_hPipe)) - debug_printf ("Failed to Impersonate the client, (%lu)", - GetLastError ()); - } - // verbose: debug_printf ("I am who you are"); -} - -void -transport_layer_pipes::revert_to_self () -{ - assert (_is_accepted_endpoint); - - RevertToSelf (); - // verbose: debug_printf ("I am who I yam"); -} - -#endif /* !__INSIDE_CYGWIN__ */ diff --git a/winsup/cygwin/cygthread.h b/winsup/cygwin/cygthread.h index 57b50e20af3..b8566d4c7e0 100644 --- a/winsup/cygwin/cygthread.h +++ b/winsup/cygwin/cygthread.h @@ -18,12 +18,12 @@ class cygthread LPTHREAD_START_ROUTINE func; VOID *arg; bool is_freerange; - static DWORD main_thread_id; static bool exiting; static DWORD WINAPI stub (VOID *); static DWORD WINAPI simplestub (VOID *); void terminate_thread (); public: + static DWORD main_thread_id; static const char * name (DWORD = 0); cygthread (LPTHREAD_START_ROUTINE, LPVOID, const char *); cygthread () {}; diff --git a/winsup/cygwin/dlmalloc.c b/winsup/cygwin/dlmalloc.c new file mode 100644 index 00000000000..1fb349221a3 --- /dev/null +++ b/winsup/cygwin/dlmalloc.c @@ -0,0 +1,3894 @@ +/* + * To do: + * - strdup? maybe shouldn't bother yet, it seems difficult to get includes + * right using dlmalloc.h + * - add STD_C prototyping + * - adhere to comment conventions + * - maybe fix ALLOCFILL vs. MOATFILL in do_init_realloced_chunk() + * - keep a list of mmaped regions for checking in malloc_update_mallinfo() + * - I think memalign() is wrong: it aligns the chunk rather than the memory + * portion of the chunk. + * - "& -alignment" in memalign() is suspect: should use "& ~alignment" + * instead? + * - malloc.h doesn't need malloc_COPY or probably a bunch of other stuff + * - add mallopt options for e.g. fill? + * - come up with a non-BBC version of M_C + * - document necessity of checking chunk address in do_check_chunk prior to + * accessing any of its fields + * Done: + * minor speedup due to extend check before mremap + * minor speedup due to returning malloc() result in memalign() if aligned + * made malloc_update_mallinfo() check alloced regions at start of sbrk area + * fixed bug: After discovering foreign sbrk, if old_top was MINSIZE, would + * reduce old_top_size to 0, thus making inuse(old_top) return 0; other + * functions would consequently attempt to access old_top->{fd,bk}, which + * were invalid. This is in malloc_extend_top(), in the "double + * fencepost" section. + * Documentation: + * malloc_usable_size(P) is equivalent to realloc(P, malloc_usable_size(P)) + * + * $Log$ + * Revision 1.5.52.1 2003/09/02 02:31:08 cgf + * merge from trunk + * + * Revision 1.6 2003/08/31 18:26:58 cgf + * * Makefile.in (MALLOC_OFILES): Always fill in with correct malloc object. + * * configure.in: Fill in MALLOC_OFILES with either debugging or regular malloc. + * * configure: Regenerate. + * * dlmalloc.c: Make various fruitless changes to attempt to get to work. + * * dlmalloc.h: Ditto. + * * malloc.cc (free): Check malloc pool when debugging. + * * path.cc (win32_device_name): Eliminate compiler warning. + * * sigproc.cc (sig_dispatch_pending): Remove use of was_pending. Let + * thisframe.call_signal_handler decide if handler should be called rather than + * using bogus was_pending check. + * * exceptions.cc (interrupt_setup): Remove accidentally checked in debugging + * code. + * * heap.cc (sbrk): Save rounded addess in user_heap_max. + * + * Revision 1.5 2001/10/03 03:49:25 cgf + * * cygheap.cc (cfree): Remove malloc debugging probe. + * * dlmalloc.c (errprint): Remove abort() call which causes interesting error + * message printing to abort prematurely. + * * environ.cc: Sprinkle MALLOC_CHECKs liberally throughout. + * (_addenv): Allocate two empty elements at end of environ to + * (apparently) work around problems with some buggy applications. + * (winenv): Avoid calling alloca if no forced environment variable is present. + * + * * exceptions.cc (open_stackdumpfile): Don't print "Dumping stack trace to..." + * when running in a cygwin environment (i.e., the parent is a cygwin process). + * + * * dtable.cc (dtable::init_std_file_from_handle): Move device type detection + * code from build_fhandler here since it is only used by this function. + * (dtable::build_fhandler_from_name): New method. Renamed from + * dtable::build_fhandler. + * (dtable::build_fhandler): Use build_fhandler_from_name. + * (cygwin_attach_handle_to_fd): Ditto. + * * syscalls.cc (_open): Ditto. + * (stat_worker): Ditto. + * * dtable.h (dtable::build_fhandler_from_name): Rename declaration from + * dtable::build_fhandler. + * + * Revision 1.4 2001/09/07 21:32:04 cgf + * * cygheap.h (init_cygheap): Move heap pointers here. + * * include/sys/cygwin.h (perprocess): Remove heap pointers. + * * dcrt0.cc (__cygwin_user_data): Reflect obsolete perprocess stuff. + * (_dll_crt0): Don't initialize heap pointers. + * (cygwin_dll_init): Ditto. + * (release_upto): Use heap pointers from cygheap. + * * heap.h: Ditto. + * * fork.cc (fork_parent): Ditto. Don't set heap pointers in ch. + * (fork_child): Remove obsolete sigproc_fixup_after_fork. + * * shared.cc (memory_init): Reorganize so that cygheap initialization is called + * prior to regular heap since regular heap uses cygheap now. + * * sigproc.cc (proc_subproc): Eliminate zombies allocation. + * (sigproc_init): Move zombies alloation here. Don't free up array on fork, just + * reuse it. + * (sigproc_fixup_after_fork): Eliminate. + * * sigproc.h: Ditto. + * * include/cygwin/version.h: Reflect change to perprocess structure. + * + * Revision 1.3 2001/06/26 14:47:48 cgf + * * mmap.cc: Clean up *ResourceLock calls throughout. + * * thread.cc (pthread_cond::TimedWait): Check for WAIT_TIMEOUT as well as + * WAIT_ABANDONED. + * (__pthread_cond_timedwait): Calculate a relative wait from the abstime + * parameter. + * + * Revision 1.2 2001/06/24 22:26:49 cgf + * forced commit + * + * Revision 1.1 2001/04/24 15:25:30 duda + * * dlmalloc.c: New file. Port of Doug Lea's malloc + * * dlmalloc.h: Ditto. + * * Makefile.in: Add support for MALLOC_DEBUG + * * config.h.in: Ditto. + * * winsup.h: Ditto. + * * configure.in: Add --enable-malloc-debugging option. + * * configure: Regenerate. + * * debug.h: Include declarations for debugging malloc. + * * tty.cc (grantpt): Fix definition. + * (unlockpt): Ditto. + * + * Revision 1.1 1997/12/24 18:34:47 nsd + * Initial revision + * + */ +/* ---------- To make a malloc.h, start cutting here ------------ */ + +/* + A version of malloc/free/realloc written by Doug Lea and released to the + public domain. Send questions/comments/complaints/performance data + to dl@cs.oswego.edu + +* VERSION 2.6.4 Thu Nov 28 07:54:55 1996 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://g.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Why use this malloc? + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and tunable. + Consistent balance across these factors results in a good general-purpose + allocator. For a high-level description, see + http://g.oswego.edu/dl/html/malloc.html + +* Synopsis of public routines + + (Much fuller descriptions are contained in the program documentation below.) + + malloc(size_t n); + Return a pointer to a newly allocated chunk of at least n bytes, or null + if no space is available. + free(Void_t* p); + Release the chunk of memory pointed to by p, or no effect if p is null. + realloc(Void_t* p, size_t n); + Return a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. The returned pointer may or may not be + the same as p. If p is null, equivalent to malloc. Unless the + #define realloc_ZERO_BYTES_FREES below is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + memalign(size_t alignment, size_t n); + Return a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument, which must be a power of + two. + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system (or as near to this as can be figured out from + all the includes/defines below.) + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + calloc(size_t unit, size_t quantity); + Returns a pointer to quantity * unit bytes, with all locations + set to zero. + cfree(Void_t* p); + Equivalent to free(p). + malloc_trim(size_t pad); + Release all but pad bytes of freed top-most memory back + to the system. Return 1 if successful, else 0. + malloc_usable_size(Void_t* p); + Report the number usable allocated bytes associated with allocated + chunk p. This may or may not report more bytes than were requested, + due to alignment and minimum size constraints. + malloc_stats(); + Prints brief summary statistics on stderr. + mallinfo() + Returns (by copy) a struct containing various summary statistics. + mallopt(int parameter_number, int parameter_value) + Changes one of the tunable parameters described below. Returns + 1 if successful in changing the parameter, else 0. + +* Vital statistics: + + Alignment: 8-byte + 8 byte alignment is currently hardwired into the design. This + seems to suffice for all current machines and C compilers. + + Assumed pointer representation: 4 or 8 bytes + Code for 8-byte pointers is untested by me but has worked + reliably by Wolfram Gloger, who contributed most of the + changes supporting this. + + Assumed size_t representation: 4 or 8 bytes + Note that size_t is allowed to be 4 bytes even if pointers are 8. + + Minimum overhead per allocated chunk: 4 or 8 bytes + Each malloced chunk has a hidden overhead of 4 bytes holding size + and status information. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) + 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) + + When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte + ptrs but 4 byte size) or 24 (for 8/8) additional bytes are + needed; 4 (8) for a trailing size field + and 8 (16) bytes for free list pointers. Thus, the minimum + allocatable size is 16/24/32 bytes. + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + + Maximum allocated size: 4-byte size_t: 2^31 - 8 bytes + 8-byte size_t: 2^63 - 16 bytes + + It is assumed that (possibly signed) size_t bit values suffice to + represent chunk sizes. `Possibly signed' is due to the fact + that `size_t' may be defined on a system as either a signed or + an unsigned type. To be conservative, values that would appear + as negative numbers are avoided. + Requests for sizes with a negative sign bit will return a + minimum-sized chunk. + + Maximum overhead wastage per allocated chunk: normally 15 bytes + + Alignnment demands, plus the minimum allocatable size restriction + make the normal worst-case wastage 15 bytes (i.e., up to 15 + more bytes will be allocated than were requested in malloc), with + two exceptions: + 1. Because requests for zero bytes allocate non-zero space, + the worst case wastage for a request of zero bytes is 24 bytes. + 2. For requests >= mmap_threshold that are serviced via + mmap(), the worst case wastage is 8 bytes plus the remainder + from a system page (the minimal mmap unit); typically 4096 bytes. + +* Limitations + + Here are some features that are NOT currently supported + + * No user-definable hooks for callbacks and the like. + * No automated mechanism for fully checking that all accesses + to malloced memory stay within their bounds. + * No support for compaction. + +* Synopsis of compile-time options: + + People have reported using previous versions of this malloc on all + versions of Unix, sometimes by tweaking some of the defines + below. It has been tested most extensively on Solaris and + Linux. It is also reported to work on WIN32 platforms. + People have also reported adapting this malloc for use in + stand-alone embedded systems. + + The implementation is in straight, hand-tuned ANSI C. Among other + consequences, it uses a lot of macros. Because of this, to be at + all usable, this code should be compiled using an optimizing compiler + (for example gcc -O2) that can simplify expressions and control + paths. + + __STD_C (default: derived from C compiler defines) + Nonzero if using ANSI-standard C compiler, a C++ compiler, or + a C compiler sufficiently close to ANSI to get away with it. + DEBUG (default: NOT defined) + Define to enable debugging. Adds fairly extensive assertion-based + checking to help track down memory errors, but noticeably slows down + execution. + realloc_ZERO_BYTES_FREES (default: NOT defined) + Define this if you think that realloc(p, 0) should be equivalent + to free(p). Otherwise, since malloc returns a unique pointer for + malloc(0), so does realloc(p, 0). + HAVE_memcpy (default: defined) + Define if you are not otherwise using ANSI STD C, but still + have memcpy and memset in your C library and want to use them. + Otherwise, simple internal versions are supplied. + USE_memcpy (default: 1 if HAVE_memcpy is defined, 0 otherwise) + Define as 1 if you want the C library versions of memset and + memcpy called in realloc and calloc (otherwise macro versions are used). + At least on some platforms, the simple macro versions usually + outperform libc versions. + HAVE_MMAP (default: defined as 1) + Define to non-zero to optionally make malloc() use mmap() to + allocate very large blocks. + HAVE_MREMAP (default: defined as 0 unless Linux libc set) + Define to non-zero to optionally make realloc() use mremap() to + reallocate very large blocks. + malloc_getpagesize (default: derived from system #includes) + Either a constant or routine call returning the system page size. + HAVE_USR_INCLUDE_malloc_H (default: NOT defined) + Optionally define if you are on a system with a /usr/include/malloc.h + that declares struct mallinfo. It is not at all necessary to + define this even if you do, but will ensure consistency. + INTERNAL_SIZE_T (default: size_t) + Define to a 32-bit type (probably `unsigned int') if you are on a + 64-bit machine, yet do not want or need to allow malloc requests of + greater than 2^31 to be handled. This saves space, especially for + very small chunks. + INTERNAL_LINUX_C_LIB (default: NOT defined) + Defined only when compiled as part of Linux libc. + Also note that there is some odd internal name-mangling via defines + (for example, internally, `malloc' is named `mALLOc') needed + when compiling in this case. These look funny but don't otherwise + affect anything. + WIN32 (default: undefined) + Define this on MS win (95, nt) platforms to compile in sbrk emulation. + LACKS_UNISTD_H (default: undefined) + Define this if your system does not have a <unistd.h>. + MORECORE (default: sbrk) + The name of the routine to call to obtain more memory from the system. + MORECORE_FAILURE (default: -1) + The value returned upon failure of MORECORE. + MORECORE_CLEARS (default 0) + True (1) if the routine mapped to MORECORE zeroes out memory (which + holds for sbrk). + DEFAULT_TRIM_THRESHOLD + DEFAULT_TOP_PAD + DEFAULT_MMAP_THRESHOLD + DEFAULT_MMAP_MAX + Default values of tunable parameters (described in detail below) + controlling interaction with host system routines (sbrk, mmap, etc). + These values may also be changed dynamically via mallopt(). The + preset defaults are those that give best performance for typical + programs/systems. + + +*/ + + + + +/* Preliminaries */ + +#include "winsup.h" + +#ifndef __STD_C +#ifdef __STDC__ +#define __STD_C 1 +#else +#if __cplusplus +#define __STD_C 1 +#else +#define __STD_C 0 +#endif /*__cplusplus*/ +#endif /*__STDC__*/ +#endif /*__STD_C*/ + +#ifndef Void_t +#if __STD_C +#define Void_t void +#else +#define Void_t char +#endif +#endif /*Void_t*/ + +#define __MALLOC_H_INCLUDED + +#if __STD_C +#include <stddef.h> /* for size_t */ +#else +#include <sys/types.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> /* needed for malloc_stats */ + + +/* + Compile-time options +*/ + + +/* + Debugging: + + Because freed chunks may be overwritten with link fields, this + malloc will often die when freed memory is overwritten by user + programs. This can be very effective (albeit in an annoying way) + in helping track down dangling pointers. + + If you compile with -DDEBUG, a number of assertion checks are + enabled that will catch more memory errors. You probably won't be + able to make much sense of the actual assertion errors, but they + should help you locate incorrectly overwritten memory. The + checking is fairly extensive, and will slow down execution + noticeably. Calling malloc_stats or mallinfo with DEBUG set will + attempt to check every non-mmapped allocated and free chunk in the + course of computing the summmaries. (By nature, mmapped regions + cannot be checked very much automatically.) + + Setting DEBUG may also be helpful if you are trying to modify + this code. The assertions in the check routines spell out in more + detail the assumptions and invariants underlying the algorithms. + +*/ + +#ifdef MALLOC_DEBUG +#define DEBUG 1 +#define DEBUG1 1 +#define DEBUG2 1 +#define DEBUG3 1 +#endif + +#if DEBUG +#include <assert.h> +#else +#define assert(x) ((void)0) +#endif + +/* + INTERNAL_SIZE_T is the word-size used for internal bookkeeping + of chunk sizes. On a 64-bit machine, you can reduce malloc + overhead by defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' + at the expense of not being able to handle requests greater than + 2^31. This limitation is hardly ever a concern; you are encouraged + to set this. However, the default version is the same as size_t. +*/ + +#ifndef INTERNAL_SIZE_T +#define INTERNAL_SIZE_T size_t +#endif + +/* + realloc_ZERO_BYTES_FREES should be set if a call to + realloc with zero bytes should be the same as a call to free. + Some people think it should. Otherwise, since this malloc + returns a unique pointer for malloc(0), so does realloc(p, 0). +*/ + + +/* #define realloc_ZERO_BYTES_FREES */ + + +/* + WIN32 causes an emulation of sbrk to be compiled in + mmap-based options are not currently supported in WIN32. +*/ + +/* #define WIN32 */ +#ifdef WIN32 +#define MORECORE wsbrk +#define HAVE_MMAP 0 +#endif + + +/* + HAVE_memcpy should be defined if you are not otherwise using + ANSI STD C, but still have memcpy and memset in your C library + and want to use them in calloc and realloc. Otherwise simple + macro versions are defined here. + + USE_memcpy should be defined as 1 if you actually want to + have memset and memcpy called. People report that the macro + versions are often enough faster than libc versions on many + systems that it is better to use them. + +*/ + +#define HAVE_memcpy + +#ifndef USE_memcpy +#ifdef HAVE_memcpy +#define USE_memcpy 1 +#else +#define USE_memcpy 0 +#endif +#endif + +#if (__STD_C || defined(HAVE_memcpy)) + +#if __STD_C +void* memset(void*, int, size_t); +void* memcpy(void*, const void*, size_t); +#else +Void_t* memset(); +Void_t* memcpy(); +#endif +#endif + +#ifndef DEBUG3 + +#if USE_memcpy + +/* The following macros are only invoked with (2n+1)-multiples of + INTERNAL_SIZE_T units, with a positive integer n. This is exploited + for fast inline execution when n is small. */ + +#define malloc_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T mzsz = (nbytes); \ + if(mzsz <= 9*sizeof(mzsz)) { \ + INTERNAL_SIZE_T* mz = (INTERNAL_SIZE_T*) (charp); \ + if(mzsz >= 5*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 7*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 9*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; }}} \ + *mz++ = 0; \ + *mz++ = 0; \ + *mz = 0; \ + } else memset((charp), 0, mzsz); \ +} while(0) + +#define malloc_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T mcsz = (nbytes); \ + if(mcsz <= 9*sizeof(mcsz)) { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) (src); \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) (dest); \ + if(mcsz >= 5*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 7*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 9*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; }}} \ + *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + *mcdst = *mcsrc ; \ + } else memcpy(dest, src, mcsz); \ +} while(0) + +#else /* !USE_memcpy */ + +/* Use Duff's device for good zeroing/copying performance. */ + +#define malloc_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T* mzp = (INTERNAL_SIZE_T*)(charp); \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mzp++ = 0; \ + case 7: *mzp++ = 0; \ + case 6: *mzp++ = 0; \ + case 5: *mzp++ = 0; \ + case 4: *mzp++ = 0; \ + case 3: *mzp++ = 0; \ + case 2: *mzp++ = 0; \ + case 1: *mzp++ = 0; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#define malloc_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) src; \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) dest; \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mcdst++ = *mcsrc++; \ + case 7: *mcdst++ = *mcsrc++; \ + case 6: *mcdst++ = *mcsrc++; \ + case 5: *mcdst++ = *mcsrc++; \ + case 4: *mcdst++ = *mcsrc++; \ + case 3: *mcdst++ = *mcsrc++; \ + case 2: *mcdst++ = *mcsrc++; \ + case 1: *mcdst++ = *mcsrc++; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#endif + +#else /* DEBUG3 */ + +/* The trailing moat invalidates the above prediction about the nbytes + parameter to malloc_ZERO and malloc_COPY. */ + +#define malloc_ZERO(charp, nbytes) \ +do { \ + char *mzp = (char *)(charp); \ + long mzn = (nbytes); \ + while (mzn--) \ + *mzp++ = '\0'; \ +} while(0) + +#define malloc_COPY(dest,src,nbytes) \ +do { \ + char *mcsrc = (char *)(src); \ + char *mcdst = (char *)(dest); \ + long mcn = (nbytes); \ + while (mcn--) \ + *mcdst++ = *mcsrc++; \ +} while(0) + +#endif /* DEBUG3 */ + +/* + Define HAVE_MMAP to optionally make malloc() use mmap() to + allocate very large blocks. These will be returned to the + operating system immediately after a free(). +*/ + +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif + +/* + Define HAVE_MREMAP to make realloc() use mremap() to re-allocate + large blocks. This is currently only possible on Linux with + kernel versions newer than 1.3.77. +*/ + +#ifndef HAVE_MREMAP +#ifdef INTERNAL_LINUX_C_LIB +#define HAVE_MREMAP 1 +#else +#define HAVE_MREMAP 0 +#endif +#endif + +#if HAVE_MMAP + +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + +#endif /* HAVE_MMAP */ + +/* + Access to system page size. To the extent possible, this malloc + manages memory from the system in page-size units. + + The following mechanics for getpagesize were adapted from + bsd/gnu getpagesize.h +*/ + +#ifndef LACKS_UNISTD_H +# include <unistd.h> +#endif + +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) +# if __STD_C + extern size_t getpagesize(void); +# else + extern size_t getpagesize(); +# endif +# define malloc_getpagesize getpagesize() +# else +# include <sys/param.h> +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else +# define malloc_getpagesize (4096) /* just guess */ +# endif +# endif +# endif +# endif +# endif +# endif +#endif + + + +/* + + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing the same kind of + information you can get from malloc_stats. It should work on + any SVID/XPG compliant system that has a /usr/include/malloc.h + defining struct mallinfo. (If you'd like to install such a thing + yourself, cut out the preliminary declarations as described above + and below and save them in a malloc.h file. But there's no + compelling reason to bother to do this.) + + The main declaration needed is the mallinfo struct that is returned + (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a + bunch of fields, most of which are not even meaningful in this + version of malloc. Some of these fields are are instead filled by + mallinfo() with other numbers that might possibly be of interest. + + HAVE_USR_INCLUDE_malloc_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else an SVID2/XPG2 compliant + version is declared below. These must be precisely the same for + mallinfo() to work. + +*/ + +/* #define HAVE_USR_INCLUDE_malloc_H */ + +#if HAVE_USR_INCLUDE_malloc_H +#include "/usr/include/malloc.h" +#else + +/* SVID2/XPG mallinfo structure */ + +struct mallinfo { + int arena; /* total space allocated from system */ + int ordblks; /* number of non-inuse chunks */ + int smblks; /* unused -- always zero */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* total space in mmapped regions */ + int usmblks; /* unused -- always zero */ + int fsmblks; /* unused -- always zero */ + int uordblks; /* total allocated space */ + int fordblks; /* total non-inuse space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + +/* SVID2/XPG mallopt options */ + +#define M_MXFAST 1 /* UNUSED in this malloc */ +#define M_NLBLKS 2 /* UNUSED in this malloc */ +#define M_GRAIN 3 /* UNUSED in this malloc */ +#define M_KEEP 4 /* UNUSED in this malloc */ + +#endif + +/* mallopt options that actually do something */ + +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 +#define M_SCANHEAP -5 +#define M_FILL + + + +#ifndef DEFAULT_TRIM_THRESHOLD +#define DEFAULT_TRIM_THRESHOLD (128 * 1024) +#endif + +/* + M_TRIM_THRESHOLD is the maximum amount of unused top-most memory + to keep before releasing via malloc_trim in free(). + + Automatic trimming is mainly useful in long-lived programs. + Because trimming via sbrk can be slow on some systems, and can + sometimes be wasteful (in cases where programs immediately + afterward allocate more large chunks) the value should be high + enough so that your overall system performance would improve by + releasing. + + The trim threshold and the mmap control parameters (see below) + can be traded off with one another. Trimming and mmapping are + two different ways of releasing unused memory back to the + system. Between these two, it is often possible to keep + system-level demands of a long-lived program down to a bare + minimum. For example, in one test suite of sessions measuring + the XF86 X server on Linux, using a trim threshold of 128K and a + mmap threshold of 192K led to near-minimal long term resource + consumption. + + If you are using this malloc in a long-lived program, it should + pay to experiment with these values. As a rough guide, you + might set to a value close to the average size of a process + (program) running on your system. Releasing this much memory + would allow such a process to run in memory. Generally, it's + worth it to tune for trimming rather tham memory mapping when a + program undergoes phases where several large chunks are + allocated and released in ways that can reuse each other's + storage, perhaps mixed with phases where there are no such + chunks at all. And in well-behaved long-lived programs, + controlling release of large blocks via trimming versus mapping + is usually faster. + + However, in most programs, these parameters serve mainly as + protection against the system-level effects of carrying around + massive amounts of unneeded memory. Since frequent calls to + sbrk, mmap, and munmap otherwise degrade performance, the default + parameters are set to relatively high values that serve only as + safeguards. + + The default trim value is high enough to cause trimming only in + fairly extreme (by current memory consumption standards) cases. + It must be greater than page size to have any useful effect. To + disable trimming completely, you can set to (unsigned long)(-1); + + +*/ + + +#ifndef DEFAULT_TOP_PAD +#define DEFAULT_TOP_PAD (0) +#endif + +/* + M_TOP_PAD is the amount of extra `padding' space to allocate or + retain whenever sbrk is called. It is used in two ways internally: + + * When sbrk is called to extend the top of the arena to satisfy + a new malloc request, this much padding is added to the sbrk + request. + + * When malloc_trim is called automatically from free(), + it is used as the `pad' argument. + + In both cases, the actual amount of padding is rounded + so that the end of the arena is always a system page boundary. + + The main reason for using padding is to avoid calling sbrk so + often. Having even a small pad greatly reduces the likelihood + that nearly every malloc request during program start-up (or + after trimming) will invoke sbrk, which needlessly wastes + time. + + Automatic rounding-up to page-size units is normally sufficient + to avoid measurable overhead, so the default is 0. However, in + systems where sbrk is relatively slow, it can pay to increase + this value, at the expense of carrying around more memory than + the program needs. + +*/ + + +#ifndef DEFAULT_MMAP_THRESHOLD +#define DEFAULT_MMAP_THRESHOLD (128 * 1024) +#endif + +/* + + M_MMAP_THRESHOLD is the request size threshold for using mmap() + to service a request. Requests of at least this size that cannot + be allocated using already-existing space will be serviced via mmap. + (If enough normal freed space already exists it is used instead.) + + Using mmap segregates relatively large chunks of memory so that + they can be individually obtained and released from the host + system. A request serviced through mmap is never reused by any + other request (at least not directly; the system may just so + happen to remap successive requests to the same locations). + + Segregating space in this way has the benefit that mmapped space + can ALWAYS be individually released back to the system, which + helps keep the system level memory demands of a long-lived + program low. Mapped memory can never become `locked' between + other chunks, as can happen with normally allocated chunks, which + menas that even trimming via malloc_trim would not release them. + + However, it has the disadvantages that: + + 1. The space cannot be reclaimed, consolidated, and then + used to service later requests, as happens with normal chunks. + 2. It can lead to more wastage because of mmap page alignment + requirements + 3. It causes malloc performance to be more dependent on host + system memory management support routines which may vary in + implementation quality and may impose arbitrary + limitations. Generally, servicing a request via normal + malloc steps is faster than going through a system's mmap. + + All together, these considerations should lead you to use mmap + only for relatively large requests. + + +*/ + + + +#ifndef DEFAULT_MMAP_MAX +#if HAVE_MMAP +#define DEFAULT_MMAP_MAX (64) +#else +#define DEFAULT_MMAP_MAX (0) +#endif +#endif + +/* + M_MMAP_MAX is the maximum number of requests to simultaneously + service using mmap. This parameter exists because: + + 1. Some systems have a limited number of internal tables for + use by mmap. + 2. In most systems, overreliance on mmap can degrade overall + performance. + 3. If a program allocates many large regions, it is probably + better off using normal sbrk-based allocation routines that + can reclaim and reallocate normal heap memory. Using a + small value allows transition into this mode after the + first few allocations. + + Setting to 0 disables all use of mmap. If HAVE_MMAP is not set, + the default value is 0, and attempts to set it to non-zero values + in mallopt will fail. +*/ + + + + +/* + + Special defines for linux libc + + Except when compiled using these special defines for Linux libc + using weak aliases, this malloc is NOT designed to work in + multithreaded applications. No semaphores or other concurrency + control are provided to ensure that multiple malloc or free calls + don't run at the same time, which could be disasterous. A single + semaphore could be used across malloc, realloc, and free (which is + essentially the effect of the linux weak alias approach). It would + be hard to obtain finer granularity. + +*/ + + +#ifdef INTERNAL_LINUX_C_LIB + +#if __STD_C + +Void_t * __default_morecore_init (ptrdiff_t); +Void_t *(*__morecore)(ptrdiff_t) = __default_morecore_init; + +#else + +Void_t * __default_morecore_init (); +Void_t *(*__morecore)() = __default_morecore_init; + +#endif + +#define MORECORE (*__morecore) +#define MORECORE_FAILURE 0 +#define MORECORE_CLEARS 1 + +#else /* INTERNAL_LINUX_C_LIB */ + +#if __STD_C +/* extern Void_t* sbrk(ptrdiff_t);*/ +#else +extern Void_t* sbrk(); +#endif + +#ifndef MORECORE +#define MORECORE sbrk +#endif + +#ifndef MORECORE_FAILURE +#define MORECORE_FAILURE -1 +#endif + +#ifndef MORECORE_CLEARS +#define MORECORE_CLEARS 0 +#endif + +#endif /* INTERNAL_LINUX_C_LIB */ + +#if defined(INTERNAL_LINUX_C_LIB) && defined(__ELF__) + +#define cALLOc __libc_calloc +#define fREe __libc_free +#define mALLOc __libc_malloc +#define mEMALIGn __libc_memalign +#define rEALLOc __libc_realloc +#define vALLOc __libc_valloc +#define pvALLOc __libc_pvalloc +#define mALLINFo __libc_mallinfo +#define mALLOPt __libc_mallopt + +#pragma weak calloc = __libc_calloc +#pragma weak free = __libc_free +#pragma weak cfree = __libc_free +#pragma weak malloc = __libc_malloc +#pragma weak memalign = __libc_memalign +#pragma weak realloc = __libc_realloc +#pragma weak valloc = __libc_valloc +#pragma weak pvalloc = __libc_pvalloc +#pragma weak mallinfo = __libc_mallinfo +#pragma weak mallopt = __libc_mallopt + +#else + +#ifndef cALLOc +#define cALLOc dlcalloc +#endif +#ifndef fREe +#define fREe dlfree +#endif +#ifndef mALLOc +#define mALLOc dlmalloc +#endif +#ifndef mEMALIGn +#define mEMALIGn dlmemalign +#endif +#ifndef rEALLOc +#define rEALLOc dlrealloc +#endif +#ifndef vALLOc +#define vALLOc dlvalloc +#endif +#ifndef pvALLOc +#define pvALLOc dlpvalloc +#endif +#ifndef mALLINFo +#define mALLINFo dlmallinfo +#endif +#ifndef mALLOPt +#define mALLOPt dlmallopt +#endif + +#endif + +/* Public routines */ + +#ifdef DEBUG2 +#define malloc(size) malloc_dbg(size, __FILE__, __LINE__) +#define free(p) free_dbg(p, __FILE__, __LINE__) +#define realloc(p, size) realloc_dbg(p, size, __FILE__, __LINE__) +#define calloc(n, size) calloc_dbg(n, size, __FILE__, __LINE__) +#define memalign(align, size) memalign_dbg(align, size, __FILE__, __LINE__) +#define valloc(size) valloc_dbg(size, __FILE__, __LINE__) +#define pvalloc(size) pvalloc_dbg(size, __FILE__, __LINE__) +#define malloc_trim(pad) malloc_trim_dbg(pad, __FILE__, __LINE__) +#define malloc_usable_size(p) malloc_usable_size_dbg(p, __FILE__, __LINE__) +#define malloc_stats(void) malloc_stats_dbg(__FILE__, __LINE__) +#define mallopt(flag, val) mallopt_dbg(flag, val, __FILE__, __LINE__) +#define mallinfo(void) mallinfo_dbg(__FILE__, __LINE__) + +#if __STD_C +Void_t* malloc_dbg(size_t, const char *, int); +void free_dbg(Void_t*, const char *, int); +Void_t* realloc_dbg(Void_t*, size_t, const char *, int); +Void_t* calloc_dbg(size_t, size_t, const char *, int); +Void_t* memalign_dbg(size_t, size_t, const char *, int); +Void_t* valloc_dbg(size_t, const char *, int); +Void_t* pvalloc_dbg(size_t, const char *, int); +int malloc_trim_dbg(size_t, const char *, int); +size_t malloc_usable_size_dbg(Void_t*, const char *, int); +void malloc_stats_dbg(const char *, int); +int mallopt_dbg(int, int, const char *, int); +struct mallinfo mallinfo_dbg(const char *, int); +#else +Void_t* malloc_dbg(); +void free_dbg(); +Void_t* realloc_dbg(); +Void_t* calloc_dbg(); +Void_t* memalign_dbg(); +Void_t* valloc_dbg(); +Void_t* pvalloc_dbg(); +int malloc_trim_dbg(); +size_t malloc_usable_size_dbg(); +void malloc_stats_dbg(); +int mallopt_dbg(); +struct mallinfo mallinfo_dbg(); +#endif /* !__STD_C */ + +#else /* !DEBUG2 */ + +#if __STD_C + +Void_t* mALLOc(size_t); +void fREe(Void_t*); +Void_t* rEALLOc(Void_t*, size_t); +Void_t* cALLOc(size_t, size_t); +Void_t* mEMALIGn(size_t, size_t); +Void_t* vALLOc(size_t); +Void_t* pvALLOc(size_t); +int malloc_trim(size_t); +size_t malloc_usable_size(Void_t*); +void malloc_stats(void); +int mALLOPt(int, int); +struct mallinfo mALLINFo(void); +#else +Void_t* mALLOc(); +void fREe(); +Void_t* rEALLOc(); +Void_t* cALLOc(); +Void_t* mEMALIGn(); +Void_t* vALLOc(); +Void_t* pvALLOc(); +int malloc_trim(); +size_t malloc_usable_size(); +void malloc_stats(); +int mALLOPt(); +struct mallinfo mALLINFo(); +#endif +#endif /* !DEBUG2 */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* ---------- To make a malloc.h, end cutting here ------------ */ + +#ifdef DEBUG2 + +#ifdef __cplusplus +extern "C" { +#endif + +#undef malloc +#undef free +#undef realloc +#undef calloc +#undef memalign +#undef valloc +#undef pvalloc +#undef malloc_trim +#undef malloc_usable_size +#undef malloc_stats +#undef mallopt +#undef mallinfo + +#if __STD_C +Void_t* mALLOc(size_t); +void fREe(Void_t*); +Void_t* rEALLOc(Void_t*, size_t); +Void_t* cALLOc(size_t, size_t); +Void_t* mEMALIGn(size_t, size_t); +Void_t* vALLOc(size_t); +Void_t* pvALLOc(size_t); +int malloc_trim(size_t); +size_t malloc_usable_size(Void_t*); +void malloc_stats(void); +int mALLOPt(int, int); +struct mallinfo mALLINFo(void); +#else +Void_t* mALLOc(); +void fREe(); +Void_t* rEALLOc(); +Void_t* cALLOc(); +Void_t* mEMALIGn(); +Void_t* vALLOc(); +Void_t* pvALLOc(); +int malloc_trim(); +size_t malloc_usable_size(); +void malloc_stats(); +int mALLOPt(); +struct mallinfo mALLINFo(); +#endif + +#include <ctype.h> /* isprint() */ +#ifdef DEBUG3 +#include <stdlib.h> /* atexit() */ +#endif + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +#endif /* DEBUG2 */ + +/* + Emulation of sbrk for WIN32 + All code within the ifdef WIN32 is untested by me. +*/ + + +#ifdef WIN32 + +#define AlignPage(add) (((add) + (malloc_getpagesize-1)) & \ + ~(malloc_getpagesize-1)) + +/* resrve 64MB to insure large contiguous space */ +#define RESERVED_SIZE (1024*1024*64) +#define NEXT_SIZE (2048*1024) +#define TOP_MEMORY ((unsigned long)2*1024*1024*1024) + +struct GmListElement; +typedef struct GmListElement GmListElement; + +struct GmListElement +{ + GmListElement* next; + void* base; +}; + +static GmListElement* head = 0; +static unsigned int gNextAddress = 0; +static unsigned int gAddressBase = 0; +static unsigned int gAllocatedSize = 0; + +static +GmListElement* makeGmListElement (void* bas) +{ + GmListElement* this; + this = (GmListElement*)(void*)LocalAlloc (0, sizeof (GmListElement)); + ASSERT (this); + if (this) + { + this->base = bas; + this->next = head; + head = this; + } + return this; +} + +void gcleanup () +{ + BOOL rval; + ASSERT ( (head == NULL) || (head->base == (void*)gAddressBase)); + if (gAddressBase && (gNextAddress - gAddressBase)) + { + rval = VirtualFree ((void*)gAddressBase, + gNextAddress - gAddressBase, + MEM_DECOMMIT); + ASSERT (rval); + } + while (head) + { + GmListElement* next = head->next; + rval = VirtualFree (head->base, 0, MEM_RELEASE); + ASSERT (rval); + LocalFree (head); + head = next; + } +} + +static +void* findRegion (void* start_address, unsigned long size) +{ + MEMORY_BASIC_INFORMATION info; + while ((unsigned long)start_address < TOP_MEMORY) + { + VirtualQuery (start_address, &info, sizeof (info)); + if (info.State != MEM_FREE) + start_address = (char*)info.BaseAddress + info.RegionSize; + else if (info.RegionSize >= size) + return start_address; + else + start_address = (char*)info.BaseAddress + info.RegionSize; + } + return NULL; + +} + + +void* wsbrk (long size) +{ + void* tmp; + if (size > 0) + { + if (gAddressBase == 0) + { + gAllocatedSize = max (RESERVED_SIZE, AlignPage (size)); + gNextAddress = gAddressBase = + (unsigned int)VirtualAlloc (NULL, gAllocatedSize, + MEM_RESERVE, PAGE_NOACCESS); + } else if (AlignPage (gNextAddress + size) > (gAddressBase + +gAllocatedSize)) + { + long new_size = max (NEXT_SIZE, AlignPage (size)); + void* new_address = (void*)(gAddressBase+gAllocatedSize); + do + { + new_address = findRegion (new_address, new_size); + + if (new_address == 0) + return (void*)-1; + + gAddressBase = gNextAddress = + (unsigned int)VirtualAlloc (new_address, new_size, + MEM_RESERVE, PAGE_NOACCESS); + // repeat in case of race condition + // The region that we found has been snagged + // by another thread + } + while (gAddressBase == 0); + + ASSERT (new_address == (void*)gAddressBase); + + gAllocatedSize = new_size; + + if (!makeGmListElement ((void*)gAddressBase)) + return (void*)-1; + } + if ((size + gNextAddress) > AlignPage (gNextAddress)) + { + void* res; + res = VirtualAlloc ((void*)AlignPage (gNextAddress), + (size + gNextAddress - + AlignPage (gNextAddress)), + MEM_COMMIT, PAGE_READWRITE); + if (res == 0) + return (void*)-1; + } + tmp = (void*)gNextAddress; + gNextAddress = (unsigned int)tmp + size; + return tmp; + } + else if (size < 0) + { + unsigned int alignedGoal = AlignPage (gNextAddress + size); + /* Trim by releasing the virtual memory */ + if (alignedGoal >= gAddressBase) + { + VirtualFree ((void*)alignedGoal, gNextAddress - alignedGoal, + MEM_DECOMMIT); + gNextAddress = gNextAddress + size; + return (void*)gNextAddress; + } + else + { + VirtualFree ((void*)gAddressBase, gNextAddress - gAddressBase, + MEM_DECOMMIT); + gNextAddress = gAddressBase; + return (void*)-1; + } + } + else + { + return (void*)gNextAddress; + } +} + +#endif + + + +/* + Type declarations +*/ + +#ifdef DEBUG3 +# define MOATWIDTH 4 /* number of guard bytes at each end of + allocated region */ +# define MOATFILL 5 /* moat fill character */ +# define ALLOCFILL 1 /* fill char for allocated */ +# define FREEFILL 2 /* and freed regions */ +#endif + +typedef struct malloc_chunk +{ + INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ + INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +#ifdef DEBUG3 + const char *file; /* file and */ + int line; /* line number of [re]allocation */ + size_t pad; /* nr pad bytes at mem end, excluding moat */ + int alloced; /* whether the chunk is allocated -- less prone + to segv than inuse(chunk) */ + char moat[MOATWIDTH]; /* actual leading moat is last MOATWIDTH bytes + of chunk header; those bytes may follow this + field due to header alignment padding */ +#endif +} Chunk; + +typedef Chunk* mchunkptr; + +/* + + malloc_chunk details: + + (The following includes lightly edited explanations by Colin Plumb.) + + Chunks of memory are maintained using a `boundary tag' method as + described in e.g., Knuth or Standish. (See the paper by Paul + Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a + survey of such techniques.) Sizes of free chunks are stored both + in the front of each chunk and at the end. This makes + consolidating fragmented chunks into bigger chunks very fast. The + size fields also hold bits representing whether chunks are free or + in use. + + An allocated chunk looks like this: + + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk, if allocated | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | User data starts here... . + . . + . (malloc_usable_space() bytes) . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Where "chunk" is the front of the chunk for the purpose of most of + the malloc code, but "mem" is the pointer that is returned to the + user. "Nextchunk" is the beginning of the next contiguous chunk. + + Chunks always begin on even word boundries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus double-word aligned. + + Free chunks are stored in circular doubly-linked lists, and look like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The P (PREV_INUSE) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + (The very first chunk allocated always has this bit set, + preventing access to non-existent (or non-owned) memory.) + + Note that the `foot' of the current chunk is actually represented + as the prev_size of the NEXT chunk. (This makes it easier to + deal with alignments etc). + + The two exceptions to all this are + + 1. The special chunk `top', which doesn't bother using the + trailing size field since there is no + next contiguous chunk that would have to index off it. (After + initialization, `top' is forced to always exist. If it would + become less than MINSIZE bytes long, it is replenished via + malloc_extend_top.) + + 2. Chunks allocated via mmap, which have the second-lowest-order + bit (IS_MMAPPED) set in their size fields. Because they are + never merged or traversed from any other chunk, they have no + foot size or inuse information. + + Available chunks are kept in any of several places (all declared below): + + * `av': An array of chunks serving as bin headers for consolidated + chunks. Each bin is doubly linked. The bins are approximately + proportionally (log) spaced. There are a lot of these bins + (128). This may look excessive, but works very well in + practice. All procedures maintain the invariant that no + consolidated chunk physically borders another one. Chunks in + bins are kept in size order, with ties going to the + approximately least recently used chunk. + + The chunks in each bin are maintained in decreasing sorted order by + size. This is irrelevant for the small bins, which all contain + the same-sized chunks, but facilitates best-fit allocation for + larger chunks. (These lists are just sequential. Keeping them in + order almost never requires enough traversal to warrant using + fancier ordered data structures.) Chunks of the same size are + linked with the most recently freed at the front, and allocations + are taken from the back. This results in LRU or FIFO allocation + order, which tends to give each chunk an equal opportunity to be + consolidated with adjacent freed chunks, resulting in larger free + chunks and less fragmentation. + + * `top': The top-most available chunk (i.e., the one bordering the + end of available memory) is treated specially. It is never + included in any bin, is used only if no other chunk is + available, and is released back to the system if it is very + large (see M_TRIM_THRESHOLD). + + * `last_remainder': A bin holding only the remainder of the + most recently split (non-top) chunk. This bin is checked + before other non-fitting chunks, so as to provide better + locality for runs of sequentially allocated chunks. + + * Implicitly, through the host system's memory mapping tables. + If supported, requests greater than a threshold are usually + serviced via calls to mmap, and then later released via munmap. + +*/ + + + + + + +/* sizes, alignments */ + +#define SIZE_SZ sizeof(INTERNAL_SIZE_T) +#define ALIGNMENT (SIZE_SZ + SIZE_SZ) +#define ALIGN_MASK (ALIGNMENT - 1) +#ifndef DEBUG3 +# define MEMOFFSET (2*SIZE_SZ) +# define OVERHEAD SIZE_SZ +# define MMAP_EXTRA SIZE_SZ /* for correct alignment */ +# define MINSIZE sizeof(Chunk) +#else +typedef union { + char strut[(sizeof(Chunk) - 1) / ALIGNMENT + 1][ALIGNMENT]; + Chunk chunk; +} PaddedChunk; +# define MEMOFFSET sizeof(PaddedChunk) +# define OVERHEAD (MEMOFFSET + MOATWIDTH) +# define MMAP_EXTRA 0 +# define MINSIZE ((OVERHEAD + ALIGN_MASK) & ~ALIGN_MASK) +#endif + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((Void_t*)((char*)(p) + MEMOFFSET)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - MEMOFFSET)) + +/* pad request bytes into a usable size, including overhead */ + +#define request2size(req) \ + ((long)((req) + OVERHEAD) < (long)MINSIZE ? MINSIZE : \ + ((req) + OVERHEAD + ALIGN_MASK) & ~ALIGN_MASK) + +/* Check if m has acceptable alignment */ + +#define aligned_OK(m) (((unsigned long)((m)) & ALIGN_MASK) == 0) + + + + +/* + Physical chunk operations +*/ + + +/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */ + +#define PREV_INUSE 0x1 + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ + +#define IS_MMAPPED 0x2 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE|IS_MMAPPED) + + +/* Ptr to next physical malloc_chunk. */ + +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->size & ~PREV_INUSE) )) + +/* Ptr to previous physical malloc_chunk */ + +#define prev_chunk(p)\ + ((mchunkptr)( ((char*)(p)) - ((p)->prev_size) )) + + +/* Treat space at ptr + offset as a chunk */ + +#define chunk_at_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) + + + + +/* + Dealing with use bits +*/ + +/* extract p's inuse bit */ + +#define inuse(p)\ +((((mchunkptr)(((char*)(p))+((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) + +/* extract inuse bit of previous chunk */ + +#define prev_inuse(p) ((p)->size & PREV_INUSE) + +/* check for mmap()'ed chunk */ + +#if HAVE_MMAP +# define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) +#else +# define chunk_is_mmapped(p) 0 +#endif + +/* set/clear chunk as in use without otherwise disturbing */ + +#define set_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE + +#define clear_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) + +/* check/set/clear inuse bits in known places */ + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE)) + + + + +/* + Dealing with size fields +*/ + +/* Get size, ignoring use bits */ + +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +/* Set size at head, without disturbing its use bit */ + +#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) + +/* Set size/use ignoring previous bits in header */ + +#define set_head(p, s) ((p)->size = (s)) + +/* Set size at footer (only when chunk is not in use) */ + +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_size = (s)) + + + + + +/* + Bins + + The bins, `av_' are an array of pairs of pointers serving as the + heads of (initially empty) doubly-linked lists of chunks, laid out + in a way so that each pair can be treated as if it were in a + malloc_chunk. (This way, the fd/bk offsets for linking bin heads + and chunks are the same). + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically + spaced. (See the table below.) The `av_' array is never mentioned + directly in the code, but instead via bin access macros. + + Bin layout: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The special chunks `top' and `last_remainder' get their own bins, + (this is implemented via yet more trickery with the av_ array), + although `top' is never properly linked to its bin since it is + always handled specially. + +*/ + +#define NAV 128 /* number of bins */ + +typedef Chunk* mbinptr; + +/* access macros */ + +#define bin_at(i) ((mbinptr)((char*)&(av_[2*(i) + 2]) - 2*SIZE_SZ)) +#define next_bin(b) ((mbinptr)((char*)(b) + 2 * sizeof(mbinptr))) +#define prev_bin(b) ((mbinptr)((char*)(b) - 2 * sizeof(mbinptr))) + +/* + The first 2 bins are never indexed. The corresponding av_ cells are instead + used for bookkeeping. This is not to save space, but to simplify + indexing, maintain locality, and avoid some initialization tests. +*/ + +#define top (bin_at(0)->fd) /* The topmost chunk */ +#define last_remainder (bin_at(1)) /* remainder from last split */ + + +/* + Because top initially points to its own bin with initial + zero size, thus forcing extension on the first malloc request, + we avoid having any special code in malloc to check whether + it even exists yet. But we still need to in malloc_extend_top. +*/ + +#define initial_top ((mchunkptr)(bin_at(0))) + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +static mbinptr av_[NAV * 2 + 2] = { + 0, 0, + IAV(0), IAV(1), IAV(2), IAV(3), IAV(4), IAV(5), IAV(6), IAV(7), + IAV(8), IAV(9), IAV(10), IAV(11), IAV(12), IAV(13), IAV(14), IAV(15), + IAV(16), IAV(17), IAV(18), IAV(19), IAV(20), IAV(21), IAV(22), IAV(23), + IAV(24), IAV(25), IAV(26), IAV(27), IAV(28), IAV(29), IAV(30), IAV(31), + IAV(32), IAV(33), IAV(34), IAV(35), IAV(36), IAV(37), IAV(38), IAV(39), + IAV(40), IAV(41), IAV(42), IAV(43), IAV(44), IAV(45), IAV(46), IAV(47), + IAV(48), IAV(49), IAV(50), IAV(51), IAV(52), IAV(53), IAV(54), IAV(55), + IAV(56), IAV(57), IAV(58), IAV(59), IAV(60), IAV(61), IAV(62), IAV(63), + IAV(64), IAV(65), IAV(66), IAV(67), IAV(68), IAV(69), IAV(70), IAV(71), + IAV(72), IAV(73), IAV(74), IAV(75), IAV(76), IAV(77), IAV(78), IAV(79), + IAV(80), IAV(81), IAV(82), IAV(83), IAV(84), IAV(85), IAV(86), IAV(87), + IAV(88), IAV(89), IAV(90), IAV(91), IAV(92), IAV(93), IAV(94), IAV(95), + IAV(96), IAV(97), IAV(98), IAV(99), IAV(100), IAV(101), IAV(102), IAV(103), + IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111), + IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119), + IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127) +}; + + + +/* field-extraction macros */ + +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* + Indexing into bins +*/ + +#define bin_index(sz) \ +(((((unsigned long)(sz)) >> 9) == 0) ? (((unsigned long)(sz)) >> 3): \ + ((((unsigned long)(sz)) >> 9) <= 4) ? 56 + (((unsigned long)(sz)) >> 6): \ + ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9): \ + ((((unsigned long)(sz)) >> 9) <= 84) ? 110 + (((unsigned long)(sz)) >> 12): \ + ((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15): \ + ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \ + 126) +/* + bins for chunks < 512 are all spaced 8 bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN 63 +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> 3) + +/* + Requests are `small' if both the corresponding and the next bin are small +*/ + +#define is_small_request(nb) (nb < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH) + + + +/* + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binblocks' is a + one-word bitvector recording whether groups of BINBLOCKWIDTH bins + have any (possibly) non-empty bins, so they can be skipped over + all at once during during traversals. The bits are NOT always + cleared as soon as all bins in a block are empty, but instead only + when all are noticed to be empty during traversal in malloc. +*/ + +#define BINBLOCKWIDTH 4 /* bins per block */ + +#define binblocks (bin_at(0)->size) /* bitvector of nonempty blocks */ + +/* bin<->block macros */ + +#define idx2binblock(ix) ((unsigned)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks |= idx2binblock(ii)) +#define clear_binblock(ii) (binblocks &= ~(idx2binblock(ii))) + + + + + +/* Other static bookkeeping data */ + +/* variables holding tunable values */ + +static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; +static unsigned long top_pad = DEFAULT_TOP_PAD; +static unsigned int n_mmaps_max = DEFAULT_MMAP_MAX; +static unsigned long mmap_threshold = DEFAULT_MMAP_THRESHOLD; +#ifdef DEBUG2 +static int scanheap = 1; +#endif + +/* The first value returned from sbrk */ +static char* sbrk_base = (char*)(-1); + +/* The maximum memory obtained from system via sbrk */ +static unsigned long max_sbrked_mem = 0; + +/* The maximum via either sbrk or mmap */ +static unsigned long max_total_mem = 0; + +/* internal working copy of mallinfo */ +static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* The total memory obtained from system via sbrk */ +#define sbrked_mem (current_mallinfo.arena) + +/* Tracking mmaps */ + +static unsigned int n_mmaps = 0; +static unsigned long mmapped_mem = 0; +#if HAVE_MMAP +static unsigned int max_n_mmaps = 0; +static unsigned long max_mmapped_mem = 0; +#endif + + + +/* + Debugging support +*/ + +#if DEBUG + +#ifndef DEBUG2 +# define unless(cond, err, p) assert(cond) +#else +# define unless(cond, err, p) do { if (!(cond)) malloc_err(err, p); } while (0) + +/* + * When debug_file is non-null, it and debug_line respectively contain the + * file and line number of the current invocation of malloc(), calloc(), + * realloc(), or free(). + */ +static const char *debug_file = NULL; +static int debug_line; + +/* + * Avoid dereferencing invalid chunk.file pointers by tracking the range of + * valid ones. Could add an "unallocated" flag to init_freed_chunk() for + * more protection, but that's probably not necessary. + */ +static const char *debug_file_min = (char *)~0; +static const char *debug_file_max = NULL; + +static char *itos(int n) +{ +#define NDIGITS (sizeof(int) * 3) + static char s[NDIGITS + 1]; + int i = NDIGITS; + do { + s[--i] = '0' + n % 10; + n /= 10; + } while (n); + return s + i; +#undef NDIGITS +} + +static int recurs = 0; + +static void errprint(const char *file, int line, const char *err) +{ + if (recurs++) { + recurs--; + return; + } + + if (file) { + write(2, file, strlen(file)); + if (line) { + write(2, ":", 1); + write(2, itos(line), strlen(itos(line))); + } + write(2, ": ", 2); + } + write(2, err, strlen(err)); + write(2, "\n", 1); + recurs--; +} + +static void malloc_err(const char *err, mchunkptr p) +{ + /* + * Display ERR on stderr, accompanying it with the caller's file and line + * number if available. If P is non-null, also attempt to display the file + * and line number at which P was most recently [re]allocated. + * + * This function's name begins with "malloc_" to make setting debugger + * breakpoints here more convenient. + */ + errprint(debug_file, debug_line, err); + +# ifndef DEBUG3 + p = 0; /* avoid "unused param" warning */ +# else + if (p && p->file && + /* avoid invalid pointers */ + debug_file_min && + p->file >= debug_file_min && + p->file <= debug_file_max && + /* try to avoid garbage file names */ + isprint(*p->file)) + errprint(p->file, p->line, "in block allocated here"); +# endif +} + +#undef malloc +#undef free +#undef realloc +#undef memalign +#undef valloc +#undef pvalloc +#undef calloc +#undef malloc_trim +#undef malloc_usable_size +#undef malloc_stats +#undef mallopt +#undef mallinfo + +static void malloc_update_mallinfo(void); + +/* + * Define front-end functions for all user-visible entry points that may + * trigger error(). + */ +#define skel(retdecl, retassign, call, retstmt) \ + retdecl \ + debug_file = file; \ + debug_line = line; \ + if (debug_file < debug_file_min) \ + debug_file_min = debug_file; \ + if (debug_file > debug_file_max) \ + debug_file_max = debug_file; \ + if (scanheap) \ + malloc_update_mallinfo(); \ + retassign call; \ + if (scanheap) \ + malloc_update_mallinfo(); \ + debug_file = NULL; \ + retstmt + +/* + * The final letter of the names of the following macros is either r or v, + * indicating that the macro handles functions with or without a return value, + * respectively. + */ +# define skelr(rettype, call) \ + skel(rettype ret;, ret = , call, return ret) +/* + * AIX's xlc compiler doesn't like empty macro args, so specify useless but + * compilable retdecl, retassign, and retstmt args: + */ +#define skelv(call) \ + skel(line += 0;, if (1), call, return) + +#define dbgargs const char *file, int line + +/* + * Front-end function definitions: + */ +Void_t* malloc_dbg(size_t bytes, dbgargs) { + skelr(Void_t*, malloc(bytes)); +} +void free_dbg(Void_t *mem, dbgargs) { + skelv(free(mem)); +} +Void_t* realloc_dbg(Void_t *oldmem, size_t bytes, dbgargs) { + skelr(Void_t*, realloc(oldmem, bytes)); +} +Void_t* memalign_dbg(size_t alignment, size_t bytes, dbgargs) { + skelr(Void_t*, dlmemalign(alignment, bytes)); +} +Void_t* valloc_dbg(size_t bytes, dbgargs) { + skelr(Void_t*, dlvalloc(bytes)); +} +Void_t* pvalloc_dbg(size_t bytes, dbgargs) { + skelr(Void_t*, dlpvalloc(bytes)); +} +Void_t* calloc_dbg(size_t n, size_t elem_size, dbgargs) { + skelr(Void_t*, calloc(n, elem_size)); +} +int malloc_trim_dbg(size_t pad, dbgargs) { + skelr(int, malloc_trim(pad)); +} +size_t malloc_usable_size_dbg(Void_t *mem, dbgargs) { + skelr(size_t, malloc_usable_size(mem)); +} +void malloc_stats_dbg(dbgargs) { + skelv(malloc_stats()); +} +int mallopt_dbg(int flag, int value, dbgargs) { + skelr(int, dlmallopt(flag, value)); +} +struct mallinfo mallinfo_dbg(dbgargs) { + skelr(struct mallinfo, dlmallinfo()); +} + +#undef skel +#undef skelr +#undef skelv +#undef dbgargs + +#endif /* DEBUG2 */ + +/* + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) +*/ + +#ifdef DEBUG3 +static int memtest(void *s, int c, size_t n) +{ + /* + * Return whether the N-byte memory region starting at S consists + * entirely of bytes with value C. + */ + unsigned char *p = (unsigned char *)s; + size_t i; + for (i = 0; i < n; i++) + if (p[i] != (unsigned char)c) + return 0; + return 1; +} +#endif /* DEBUG3 */ + +#ifndef DEBUG3 +#define check_moats(P) +#else +#define check_moats do_check_moats +static void do_check_moats(mchunkptr p) +{ + INTERNAL_SIZE_T sz = chunksize(p); + unless(memtest((char *)chunk2mem(p) - MOATWIDTH, MOATFILL, + MOATWIDTH), "region underflow", p); + unless(memtest((char *)p + sz - MOATWIDTH - p->pad, MOATFILL, + MOATWIDTH + p->pad), "region overflow", p); +} +#endif /* DEBUG3 */ + +#if __STD_C +static void do_check_chunk(mchunkptr p) +#else +static void do_check_chunk(p) mchunkptr p; +#endif +{ + /* Try to ensure legal addresses before accessing any chunk fields, in the + * hope of issuing an informative message rather than causing a segv. + * + * The following chunk_is_mmapped() call accesses p->size #if HAVE_MMAP. + * This is unavoidable without maintaining a record of mmapped regions. + */ + if (!chunk_is_mmapped(p)) + { + INTERNAL_SIZE_T sz; + + unless((char*)p >= sbrk_base, "chunk precedes sbrk_base", p); + unless((char*)p + MINSIZE <= (char*)top + chunksize(top), + "chunk past sbrk area", p); + + sz = chunksize(p); + if (p != top) + unless((char*)p + sz <= (char*)top, "chunk extends beyond top", p); + else + unless((char*)p + sz <= sbrk_base + sbrked_mem, + "chunk extends past sbrk area", p); + } + check_moats(p); +} + +#if __STD_C +static void do_check_free_chunk(mchunkptr p) +#else +static void do_check_free_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = chunksize(p); + mchunkptr next = chunk_at_offset(p, sz); + + do_check_chunk(p); + + /* Check whether it claims to be free ... */ + unless(!inuse(p), "free chunk marked inuse", p); + + /* Unless a special marker, must have OK fields */ + if ((long)sz >= (long)MINSIZE) + { + unless((sz & ALIGN_MASK) == 0, "freed size defies alignment", p); + unless(aligned_OK(chunk2mem(p)), "misaligned freed region", p); + /* ... matching footer field */ + unless(next->prev_size == sz, "chunk size mismatch", p); + /* ... and is fully consolidated */ + unless(prev_inuse(p), "free chunk not joined with prev", p); + unless(next == top || inuse(next), "free chunk not joined with next", p); + + /* ... and has minimally sane links */ + unless(p->fd->bk == p, "broken forward link", p); + unless(p->bk->fd == p, "broken backward link", p); + } + else /* markers are always of size SIZE_SZ */ + unless(sz == SIZE_SZ, "invalid small chunk size", p); +} + +#if __STD_C +static void do_check_inuse_chunk(mchunkptr p) +#else +static void do_check_inuse_chunk(p) mchunkptr p; +#endif +{ + mchunkptr next; + do_check_chunk(p); + + if (chunk_is_mmapped(p)) + return; + + /* Check whether it claims to be in use ... */ +#ifdef DEBUG3 + unless(p->alloced, "memory not allocated", p); +#endif + unless(inuse(p), "memory not allocated", p); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse(p)) + { + mchunkptr prv = prev_chunk(p); + unless(next_chunk(prv) == p, "prev link scrambled", p); + do_check_free_chunk(prv); + } + next = next_chunk(p); + if (next == top) + { + unless(prev_inuse(next), "top chunk wrongly thinks prev is unused", p); + unless(chunksize(next) >= MINSIZE, "top chunk too small", p); + } + else if (!inuse(next)) + do_check_free_chunk(next); +} + +#if __STD_C +static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) +#else +static void do_check_malloced_chunk(p, s) mchunkptr p; INTERNAL_SIZE_T s; +#endif +{ + INTERNAL_SIZE_T sz = chunksize(p); + long room = sz - s; + + do_check_inuse_chunk(p); + + /* Legal size ... */ + unless((long)sz >= (long)MINSIZE, "chunk size too small", p); + unless((sz & ALIGN_MASK) == 0, "malloced size defies alignment", p); + unless(room >= 0, "chunk size too small for contents", p); + unless(room < (long)MINSIZE, "chunk size leaves too much spare room", p); + + /* ... and alignment */ + unless(aligned_OK(chunk2mem(p)), "misaligned malloced region", p); + + + /* ... and was allocated at front of an available chunk */ + unless(prev_inuse(p), "malloced from the middle of a free chunk", p); +} + +#ifdef DEBUG3 +static void init_alloced_chunk(mchunkptr p, size_t bytes) +{ + Void_t* mem = chunk2mem(p); + p->file = debug_file; + p->line = debug_line; + p->pad = chunksize(p) - OVERHEAD - bytes; + p->alloced = 1; + memset((char *)mem + bytes, MOATFILL, p->pad + MOATWIDTH); +} + +static void do_init_malloced_chunk(mchunkptr p, size_t bytes) +{ + Void_t* mem = chunk2mem(p); + init_alloced_chunk(p, bytes); + memset((char *)mem - MOATWIDTH, MOATFILL, MOATWIDTH); + memset(mem, ALLOCFILL, bytes); +} + +static void do_init_realloced_chunk(mchunkptr p, size_t bytes, + INTERNAL_SIZE_T oldsize) +{ + Void_t* mem = chunk2mem(p); + INTERNAL_SIZE_T newsize = chunksize(p); + init_alloced_chunk(p, bytes); + if (oldsize < newsize) + /* This incorrectly leaves the leading pad area of the old trailing moat + * set to MOATFILL rather than ALLOCFILL. An alternative is to save the + * old p->pad in rEALLOc() below and pass it to this function. + */ + memset((char *)mem + oldsize - OVERHEAD, ALLOCFILL, + bytes - (oldsize - OVERHEAD)); +} + +static void do_check_freefill(mchunkptr p, long newsize, + INTERNAL_SIZE_T oldsize) +{ + /* The first newsize bytes of oldsize-byte chunk p are about to be + * allocated. Issue a warning if any freefill locations in p that are about + * to be overwritten do not contain the character FREEFILL. + */ + size_t bytes, maxbytes; + if (newsize <= 0) + return; + bytes = newsize - MEMOFFSET /* don't check p's header */ + + MEMOFFSET; /* header of split-off remainder */ + maxbytes = oldsize - OVERHEAD; + if (bytes > maxbytes) + bytes = maxbytes; + unless(memtest(chunk2mem(p), FREEFILL, bytes), + "detected write to freed region", p); +} + +static void do_init_freed_chunk(mchunkptr p, INTERNAL_SIZE_T freehead, + INTERNAL_SIZE_T freetail) +{ + /* freehead and freetail are the number of bytes at the beginning of p and + * end of p respectively that should already be initialized as free regions. + */ + Void_t* mem = chunk2mem(p); + size_t size = chunksize(p); + size_t bytes = size - OVERHEAD; + p->pad = 0; + p->alloced = 0; + memset((char *)mem - MOATWIDTH, MOATFILL, MOATWIDTH); + memset((char *)mem + bytes, MOATFILL, MOATWIDTH); + + /* To avoid terrible O(n^2) performance when free() repeatedly grows a free + * chunk, it's important not to free-fill regions that are already + * free-filled. + */ + if (freehead + freetail < size) { + Void_t* start = !freehead ? mem : (char *)p + freehead - MOATWIDTH; + size_t len = (char *)p + size - (char *)start - + (!freetail ? MOATWIDTH : freetail - OVERHEAD); + memset(start, FREEFILL, len); + } +} + +static void do_init_freeable_chunk(mchunkptr p) +{ + /* Arrange for the subsequent fREe(p) not to generate any warnings. */ + init_alloced_chunk(p, chunksize(p) - OVERHEAD); + memset((char *)chunk2mem(p) - MOATWIDTH, MOATFILL, MOATWIDTH); +} + +static void do_maximize_chunk(mchunkptr p) +{ + if (p->pad) { + Void_t* mem = chunk2mem(p); + size_t bytes = chunksize(p) - OVERHEAD - p->pad; + memset((char *)mem + bytes, ALLOCFILL, p->pad); + p->pad = 0; + } +} + +static int do_check_init(void) +{ + /* Called from the first invocation of malloc_extend_top(), as detected by + * sbrk_base == -1. Return whether this function allocated any memory. + */ + static int state = 0; /* 1 => initializing, 2 => initialized */ + if (state == 1) + return 0; + unless(state == 0, "multiple calls to check_init", NULL); + state++; + atexit(malloc_update_mallinfo); /* calls malloc on WinNT */ + return sbrk_base != (char *)-1; +} +#endif /* DEBUG3 */ + +static mchunkptr lowest_chunk; + +#define check_free_chunk(P) do_check_free_chunk(P) +#define check_inuse_chunk(P) do_check_inuse_chunk(P) +#define check_chunk(P) do_check_chunk(P) +#define check_malloced_chunk(P,N) do_check_malloced_chunk(P,N) +#else /* !DEBUG */ +#define check_free_chunk(P) +#define check_inuse_chunk(P) +#define check_chunk(P) +#define check_malloced_chunk(P,N) +#endif /* !DEBUG */ + +#ifdef DEBUG3 +#define check_init do_check_init +#define init_malloced_chunk do_init_malloced_chunk +#define init_realloced_chunk do_init_realloced_chunk +#define check_freefill do_check_freefill +#define init_freed_chunk do_init_freed_chunk +#define init_freeable_chunk do_init_freeable_chunk +#define maximize_chunk do_maximize_chunk +#else +#define check_init() 0 +#define init_malloced_chunk(P,B) +#define init_realloced_chunk(P,B,O) +#define check_freefill(P,N,O) +#define init_freed_chunk(P,H,T) +#define init_freeable_chunk(P) +#define maximize_chunk(P) +#endif /* !DEBUG3 */ + + + +/* + Macro-based internal utilities +*/ + + +/* + Linking chunks in bin lists. + Call these only with variables, not arbitrary expressions, as arguments. +*/ + +/* + Place chunk p of size s in its bin, in size order, + putting it ahead of others of same size. +*/ + + +#define frontlink(P, S, IDX, BK, FD) \ +{ \ + if (S < MAX_SMALLBIN_SIZE) \ + { \ + IDX = smallbin_index(S); \ + mark_binblock(IDX); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ + else \ + { \ + IDX = bin_index(S); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + if (FD == BK) mark_binblock(IDX); \ + else \ + { \ + while (FD != BK && S < chunksize(FD)) FD = FD->fd; \ + BK = FD->bk; \ + } \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ +} + + +/* take a chunk off a list */ + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} \ + +/* Place p as the last remainder */ + +#define link_last_remainder(P) \ +{ \ + last_remainder->fd = last_remainder->bk = P; \ + P->fd = P->bk = last_remainder; \ +} + +/* Clear the last_remainder bin */ + +#define clear_last_remainder \ + (last_remainder->fd = last_remainder->bk = last_remainder) + + + + + + +/* Routines dealing with mmap(). */ + +#if HAVE_MMAP + +#if __STD_C +static mchunkptr mmap_chunk(size_t size) +#else +static mchunkptr mmap_chunk(size) size_t size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + mchunkptr p; + +#ifndef MAP_ANONYMOUS + static int fd = -1; +#endif + + if(n_mmaps >= n_mmaps_max) return 0; /* too many regions */ + + size = (size + MMAP_EXTRA + page_mask) & ~page_mask; + +#ifdef MAP_ANONYMOUS + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else /* !MAP_ANONYMOUS */ + if (fd < 0) + { + fd = open("/dev/zero", O_RDWR); + if(fd < 0) return 0; + } + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + + if(p == (mchunkptr)-1) return 0; + + n_mmaps++; + if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps; + + /* We demand that eight bytes into a page must be 8-byte aligned. */ + assert(aligned_OK(chunk2mem(p))); + + /* The offset to the start of the mmapped region is stored + * in the prev_size field of the chunk; normally it is zero, + * but that can be changed in memalign(). + */ + p->prev_size = 0; + set_head(p, size|IS_MMAPPED); + + mmapped_mem += size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#if __STD_C +static void munmap_chunk(mchunkptr p) +#else +static void munmap_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T size = chunksize(p); + int ret; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((p->prev_size + size) & (malloc_getpagesize-1)) == 0); + + n_mmaps--; + mmapped_mem -= (size + p->prev_size); + + ret = munmap((char *)p - p->prev_size, size + p->prev_size); + + /* munmap returns non-zero on failure */ + assert(ret == 0); +} + +#if HAVE_MREMAP + +#if __STD_C +static mchunkptr mremap_chunk(mchunkptr p, size_t new_size) +#else +static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + INTERNAL_SIZE_T offset = p->prev_size; + INTERNAL_SIZE_T size = chunksize(p); + char *cp; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((size + offset) & (malloc_getpagesize-1)) == 0); + + new_size = (new_size + offset + MMAP_EXTRA + page_mask) & ~page_mask; + + cp = (char *)mremap((char *)p - offset, size + offset, new_size, 1); + + if (cp == (char *)-1) return 0; + + p = (mchunkptr)(cp + offset); + + assert(aligned_OK(chunk2mem(p))); + + assert(p->prev_size == offset); + set_head(p, (new_size - offset)|IS_MMAPPED); + + mmapped_mem -= size + offset; + mmapped_mem += new_size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#endif /* HAVE_MREMAP */ + +#endif /* HAVE_MMAP */ + + + + +/* + Extend the top-most chunk by obtaining memory from system. + Main interface to sbrk (but see also malloc_trim). +*/ + +#if __STD_C +static void malloc_extend_top(INTERNAL_SIZE_T nb) +#else +static void malloc_extend_top(nb) INTERNAL_SIZE_T nb; +#endif +{ + char* lim; /* return value from sbrk */ + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of sbrked space */ + INTERNAL_SIZE_T correction; /* bytes for 2nd sbrk call */ + char* new_lim; /* return of 2nd sbrk call */ + INTERNAL_SIZE_T top_size; /* new size of top chunk */ + + mchunkptr old_top = top; /* Record state of old top */ + INTERNAL_SIZE_T old_top_size = chunksize(old_top); + char* old_end = (char*)(chunk_at_offset(old_top, old_top_size)); + + /* Pad request with top_pad plus minimal overhead */ + + INTERNAL_SIZE_T sbrk_size = nb + top_pad + MINSIZE; + unsigned long pagesz = malloc_getpagesize; + + /* If not the first time through, round to preserve page boundary */ + /* Otherwise, we need to correct to a page size below anyway. */ + /* (We also correct below if an intervening foreign sbrk call.) */ + + if (sbrk_base != (char*)(-1)) + sbrk_size = (sbrk_size + (pagesz - 1)) & ~(pagesz - 1); + + else if (check_init()) { + if (chunksize(top) - nb < (long)MINSIZE) + malloc_extend_top(nb); + return; + } + + lim = (char*)(MORECORE (sbrk_size)); + + /* Fail if sbrk failed or if a foreign sbrk call killed our space */ + if (lim == (char*)(MORECORE_FAILURE) || + (lim < old_end && old_top != initial_top)) + return; + + sbrked_mem += sbrk_size; + + if (lim == old_end) /* can just add bytes to current top */ + { + top_size = sbrk_size + old_top_size; + set_head(top, top_size | PREV_INUSE); + } + else + { +#ifdef SBRKDBG + INTERNAL_SIZE_T padding = (char *)sbrk (0) - (lim + sbrk_size); + sbrk_size += padding; + sbrked_mem += padding; +#endif + + if (sbrk_base == (char*)(-1)) /* First time through. Record base */ + sbrk_base = lim; + else /* Someone else called sbrk(). Count those bytes as sbrked_mem. */ + sbrked_mem += lim - (char*)old_end; + + /* Guarantee alignment of first new chunk made from this space */ + front_misalign = (unsigned long)chunk2mem(lim) & ALIGN_MASK; + if (front_misalign > 0) + { + correction = (ALIGNMENT) - front_misalign; + lim += correction; + } + else + correction = 0; + + /* Guarantee the next brk will be at a page boundary */ + correction += pagesz - ((unsigned long)(lim + sbrk_size) & (pagesz - 1)); + + /* Allocate correction */ + new_lim = (char*)(MORECORE (correction)); + if (new_lim == (char*)(MORECORE_FAILURE)) return; + + sbrked_mem += correction; + + top = (mchunkptr)lim; + top_size = new_lim - lim + correction; + set_head(top, top_size | PREV_INUSE); +#if DEBUG + lowest_chunk = top; +#endif + +#ifdef OTHER_SBRKS + if (old_top != initial_top) + { + + /* There must have been an intervening foreign sbrk call. */ + /* A double fencepost is necessary to prevent consolidation */ + + /* If not enough space to do this, then user did something very wrong */ + if (old_top_size < MINSIZE) + { + set_head(top, PREV_INUSE); /* will force null return from malloc */ + return; + } + + old_top_size -= 2*SIZE_SZ; + chunk_at_offset(old_top, old_top_size )->size = + SIZE_SZ|PREV_INUSE; + chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size = + SIZE_SZ|PREV_INUSE; + set_head_size(old_top, old_top_size); + /* If possible, release the rest. */ + if (old_top_size >= MINSIZE) { + init_freeable_chunk(old_top); + fREe(chunk2mem(old_top)); + } + } +#endif /* OTHER_SBRKS */ + } + + init_freed_chunk(top, old_top == initial_top ? old_top_size : 0, 0); + + if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem) + max_sbrked_mem = sbrked_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + + /* We always land on a page boundary */ + assert(((unsigned long)((char*)top + top_size) & (pagesz - 1)) == 0); +} + + + + +/* Main public routines */ + + +/* + Malloc Algorthim: + + The requested size is first converted into a usable form, `nb'. + This currently means to add 4 bytes overhead plus possibly more to + obtain 8-byte alignment and/or to obtain a size of at least + MINSIZE (currently 16 bytes), the smallest allocatable size. + (All fits are considered `exact' if they are within MINSIZE bytes.) + + From there, the first successful of the following steps is taken: + + 1. The bin corresponding to the request size is scanned, and if + a chunk of exactly the right size is found, it is taken. + + 2. The most recently remaindered chunk is used if it is big + enough. This is a form of (roving) first fit, used only in + the absence of exact fits. Runs of consecutive requests use + the remainder of the chunk used for the previous such request + whenever possible. This limited use of a first-fit style + allocation strategy tends to give contiguous chunks + coextensive lifetimes, which improves locality and can reduce + fragmentation in the long run. + + 3. Other bins are scanned in increasing size order, using a + chunk big enough to fulfill the request, and splitting off + any remainder. This search is strictly by best-fit; i.e., + the smallest (with ties going to approximately the least + recently used) chunk that fits is selected. + + 4. If large enough, the chunk bordering the end of memory + (`top') is split off. (This use of `top' is in accord with + the best-fit search rule. In effect, `top' is treated as + larger (and thus less well fitting) than any other available + chunk since it can be extended to be as large as necessary + (up to system limitations). + + 5. If the request size meets the mmap threshold and the + system supports mmap, and there are few enough currently + allocated mmapped regions, and a call to mmap succeeds, + the request is allocated via direct memory mapping. + + 6. Otherwise, the top of memory is extended by + obtaining more space from the system (normally using sbrk, + but definable to anything else via the MORECORE macro). + Memory is gathered from the system (in system page-sized + units) in a way that allows chunks obtained across different + sbrk calls to be consolidated, but does not require + contiguous memory. Thus, it should be safe to intersperse + mallocs with other sbrk calls. + + + All allocations are made from the the `lowest' part of any found + chunk. (The implementation invariant is that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use chunk, + or the base of its memory arena.) + +*/ + +#if __STD_C +Void_t* mALLOc(size_t bytes) +#else +Void_t* mALLOc(bytes) size_t bytes; +#endif +{ + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T victim_size; /* its size */ + int idx; /* index for bin traversal */ + mbinptr bin; /* associated bin */ + mchunkptr remainder; /* remainder from a split */ + long remainder_size; /* its size */ + int remainder_index; /* its bin index */ + unsigned long block; /* block traverser bit */ + int startidx; /* first bin of a traversed block */ + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + mbinptr q; /* misc temp */ + + INTERNAL_SIZE_T nb = request2size(bytes); /* padded request size; */ + + /* Check for exact match in a bin */ + + if (is_small_request(nb)) /* Faster version for small requests */ + { + idx = smallbin_index(nb); + + /* No traversal or size check necessary for small bins. */ + + q = bin_at(idx); + victim = last(q); + + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_freefill(victim, victim_size, victim_size); + init_malloced_chunk(victim, bytes); + check_malloced_chunk(victim, nb); + + return chunk2mem(victim); + } + + idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */ + + } + else + { + idx = bin_index(nb); + bin = bin_at(idx); + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* too big */ + { + --idx; /* adjust to rescan below after checking last remainder */ + break; + } + + else if (remainder_size >= 0) /* exact fit */ + { + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_freefill(victim, victim_size, victim_size); + init_malloced_chunk(victim, bytes); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + } + + ++idx; + + } + + /* Try to use the last split-off remainder */ + + if ( (victim = last_remainder->fd) != last_remainder) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* re-split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + check_freefill(victim, nb, victim_size); + init_malloced_chunk(victim, bytes); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + init_freed_chunk(remainder, remainder_size, 0); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_freefill(victim, victim_size, victim_size); + init_malloced_chunk(victim, bytes); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + /* Else place in bin */ + + frontlink(victim, victim_size, remainder_index, bck, fwd); + } + + /* + If there are any possibly nonempty big-enough blocks, + search for best fitting chunk by scanning bins in blockwidth units. + */ + + if ( (block = idx2binblock(idx)) <= binblocks) + { + + /* Get to the first marked block */ + + if ( (block & binblocks) == 0) + { + /* force to an even block boundary */ + idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH; + block <<= 1; + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + + /* For each possibly nonempty block ... */ + for (;;) + { + startidx = idx; /* (track incomplete blocks) */ + q = bin = bin_at(idx); + + /* For each bin in this block ... */ + do + { + /* Find and use first big enough chunk ... */ + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = victim_size - nb; + + if (remainder_size >= (long)MINSIZE) /* split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + check_freefill(victim, nb, victim_size); + unlink(victim, bck, fwd); + init_malloced_chunk(victim, bytes); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + init_freed_chunk(remainder, remainder_size, 0); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + else if (remainder_size >= 0) /* take */ + { + check_freefill(victim, victim_size, victim_size); + set_inuse_bit_at_offset(victim, victim_size); + unlink(victim, bck, fwd); + init_malloced_chunk(victim, bytes); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + + } while ((++idx & (BINBLOCKWIDTH - 1)) != 0); + + /* Clear out the block bit. */ + + do /* Possibly backtrack to try to clear a partial block */ + { + if ((startidx & (BINBLOCKWIDTH - 1)) == 0) + { + binblocks &= ~block; + break; + } + --startidx; + q = prev_bin(q); + } while (first(q) == q); + + /* Get to the next possibly nonempty block */ + + if ( (block <<= 1) <= binblocks && (block != 0) ) + { + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + else + break; + } + } + + + /* Try to use top chunk */ + + /* Require that there be a remainder, ensuring top always exists */ + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + { + +#if HAVE_MMAP + /* If big and would otherwise need to extend, try to use mmap instead */ + if ((unsigned long)nb >= (unsigned long)mmap_threshold && + (victim = mmap_chunk(nb)) != 0) { + init_malloced_chunk(victim, bytes); + return chunk2mem(victim); + } +#endif + + /* Try to extend */ + malloc_extend_top(nb); + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + return 0; /* propagate failure */ + } + + victim = top; + set_head(victim, nb | PREV_INUSE); + check_freefill(victim, nb, nb + remainder_size); + init_malloced_chunk(victim, bytes); + top = chunk_at_offset(victim, nb); + set_head(top, remainder_size | PREV_INUSE); + init_freed_chunk(top, remainder_size, 0); + check_malloced_chunk(victim, nb); + return chunk2mem(victim); + +} + + + + +/* + + free() algorithm : + + cases: + + 1. free(0) has no effect. + + 2. If the chunk was allocated via mmap, it is release via munmap(). + + 3. If a returned chunk borders the current high end of memory, + it is consolidated into the top, and if the total unused + topmost memory exceeds the trim threshold, malloc_trim is + called. + + 4. Other chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). + +*/ + + +#if __STD_C +void fREe(Void_t* mem) +#else +void fREe(mem) Void_t* mem; +#endif +{ + mchunkptr p; /* chunk corresponding to mem */ + INTERNAL_SIZE_T hd; /* its head field */ + INTERNAL_SIZE_T sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsz; /* its size */ + INTERNAL_SIZE_T prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + + if (mem == 0) /* free(0) has no effect */ + return; + + p = mem2chunk(mem); + check_inuse_chunk(p); + + hd = p->size; + +#if HAVE_MMAP + if (hd & IS_MMAPPED) /* release mmapped memory. */ + { + munmap_chunk(p); + return; + } +#endif + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + prevsz = 0; /* avoid compiler warnings */ + + if (next == top) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -(long)prevsz); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top = p; + init_freed_chunk(top, !(hd & PREV_INUSE) ? prevsz : 0, nextsz); + if ((unsigned long)(sz) >= trim_threshold) + malloc_trim(top_pad); + return; + } + + set_head(next, nextsz); /* clear inuse bit */ + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -(long)prevsz); + sz += prevsz; + + if (p->fd == last_remainder) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder) /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(p); + } + else + unlink(next, bck, fwd); + } + + + set_head(p, sz | PREV_INUSE); + set_foot(p, sz); + if (!islr) + frontlink(p, sz, idx, bck, fwd); + init_freed_chunk(p, !(hd & PREV_INUSE) ? prevsz : 0, + !inuse_bit_at_offset(next, nextsz) ? nextsz : 0); +} + + + + + +/* + + Realloc algorithm: + + Chunks that were obtained via mmap cannot be extended or shrunk + unless HAVE_MREMAP is defined, in which case mremap is used. + Otherwise, if their reallocation is for additional space, they are + copied. If for less, they are just left alone. + + Otherwise, if the reallocation is for additional space, and the + chunk can be extended, it is, else a malloc-copy-free sequence is + taken. There are several different ways that a chunk could be + extended. All are tried: + + * Extending forward into following adjacent free chunk. + * Shifting backwards, joining preceding adjacent space + * Both shifting backwards and extending forward. + * Extending into newly sbrked space + + Unless the #define realloc_ZERO_BYTES_FREES is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + + If the reallocation is for less space, and the new request is for + a `small' (<512 bytes) size, then the newly unused space is lopped + off and freed. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is no longer supported. + I don't know of any programs still relying on this feature, + and allowing it would also allow too many other incorrect + usages of realloc to be sensible. + + +*/ + + +#if __STD_C +Void_t* rEALLOc(Void_t* oldmem, size_t bytes) +#else +Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + + mchunkptr oldp; /* chunk corresponding to oldmem */ + INTERNAL_SIZE_T oldsize; /* its size */ + + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + Void_t* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + INTERNAL_SIZE_T nextsize; /* its size */ + + mchunkptr prev; /* previous contiguous chunk before oldp */ + INTERNAL_SIZE_T prevsize; /* its size */ + + mchunkptr remainder; /* holds split off extra space from newp */ + INTERNAL_SIZE_T remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + +#ifdef realloc_ZERO_BYTES_FREES + if (bytes == 0) { fREe(oldmem); return 0; } +#endif + + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == 0) return mALLOc(bytes); + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + + nb = request2size(bytes); + + check_inuse_chunk(oldp); + +#if HAVE_MMAP + if (chunk_is_mmapped(oldp)) + { + if (oldsize - MMAP_EXTRA >= nb) { + init_realloced_chunk(oldp, bytes, oldsize); + return oldmem; /* do nothing */ + } +#if HAVE_MREMAP + newp = mremap_chunk(oldp, nb); + if (newp) { + init_realloced_chunk(newp, bytes, oldsize); + return chunk2mem(newp); + } +#endif + /* Must alloc, copy, free. */ + newmem = mALLOc(bytes); + if (newmem == 0) return 0; /* propagate failure */ + malloc_COPY(newmem, oldmem, oldsize - OVERHEAD - MMAP_EXTRA); + munmap_chunk(oldp); + return newmem; + } +#endif + + if (oldsize < nb) + { + + /* Try expanding forward */ + + next = chunk_at_offset(oldp, oldsize); + if (next == top || !inuse(next)) + { + nextsize = chunksize(next); + + /* Forward into top only if a remainder */ + if (next == top) + { + if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) + { + check_freefill(next, nb - oldsize, nextsize); + newsize += nextsize; + top = chunk_at_offset(oldp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + init_freed_chunk(top, newsize - nb, 0); + set_head_size(oldp, nb); + init_realloced_chunk(oldp, bytes, oldsize); + return chunk2mem(oldp); + } + } + + /* Forward into next chunk */ + else if (((long)(nextsize + newsize) >= (long)nb)) + { + check_freefill(next, nb - oldsize, nextsize); + unlink(next, bck, fwd); + newsize += nextsize; + goto split; + } + } + else + { + next = 0; + nextsize = 0; + } + + /* Try shifting backwards. */ + + if (!prev_inuse(oldp)) + { + prev = prev_chunk(oldp); + prevsize = chunksize(prev); + + /* try forward + backward first to save a later consolidation */ + + if (next != 0) + { + /* into top */ + if (next == top) + { + if ((long)(nextsize + prevsize + newsize) >= (long)(nb + MINSIZE)) + { + check_freefill(prev, nb, prevsize); + check_freefill(next, nb - (prevsize + newsize), nextsize); + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize + nextsize; + newmem = chunk2mem(newp); + malloc_COPY(newmem, oldmem, oldsize - OVERHEAD); + top = chunk_at_offset(newp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + init_freed_chunk(top, newsize - nb, 0); + set_head_size(newp, nb); + init_realloced_chunk(newp, bytes, oldsize); + return newmem; + } + } + + /* into next chunk */ + else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) + { + check_freefill(prev, nb, prevsize); + check_freefill(next, nb - (prevsize + newsize), nextsize); + unlink(next, bck, fwd); + unlink(prev, bck, fwd); + newp = prev; + newsize += nextsize + prevsize; + newmem = chunk2mem(newp); + malloc_COPY(newmem, oldmem, oldsize - OVERHEAD); + goto split; + } + } + + /* backward only */ + if (prev != 0 && (long)(prevsize + newsize) >= (long)nb) + { + check_freefill(prev, nb, prevsize); + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize; + newmem = chunk2mem(newp); + malloc_COPY(newmem, oldmem, oldsize - OVERHEAD); + goto split; + } + } + + /* Must allocate */ + + newmem = mALLOc (bytes); + + if (newmem == 0) /* propagate failure */ + return 0; + + /* Avoid copy if newp is next chunk after oldp. */ + /* (This can only happen when new chunk is sbrk'ed.) */ + + if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) + { + newsize += chunksize(newp); + newp = oldp; + goto split; + } + + /* Otherwise copy, free, and exit */ + malloc_COPY(newmem, oldmem, oldsize - OVERHEAD); + fREe(oldmem); + return newmem; + } + + + split: /* split off extra room in old or expanded chunk */ + + if (newsize - nb >= MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + remainder_size = newsize - nb; + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + init_malloced_chunk(remainder, remainder_size - OVERHEAD); + fREe(chunk2mem(remainder)); /* let free() deal with it */ + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + init_realloced_chunk(newp, bytes, oldsize); + check_inuse_chunk(newp); + return chunk2mem(newp); +} + + + + +/* + + memalign algorithm: + + memalign requests more than enough space from malloc, finds a spot + within that chunk that meets the alignment request, and then + possibly frees the leading and trailing space. + + The alignment argument must be a power of two. This property is not + checked by memalign, so misuse may result in random runtime errors. + + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + +*/ + + +#if __STD_C +Void_t* mEMALIGn(size_t alignment, size_t bytes) +#else +Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char* m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char* lim; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space befor alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + long remainder_size; /* its size */ + + /* If need less alignment than we give anyway, just relay to malloc */ + + if (alignment <= ALIGNMENT) return mALLOc(bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + + if (alignment < MINSIZE) alignment = MINSIZE; + + /* Call malloc with worst case padding to hit alignment. */ + + nb = request2size(bytes); + m = (char*)mALLOc(nb + alignment + MINSIZE); + + if (m == 0) return 0; /* propagate failure */ + + p = mem2chunk(m); + + if ((((unsigned long)(m)) % alignment) == 0) /* aligned */ + { + init_realloced_chunk(p, bytes, chunksize(p)); + return chunk2mem(p); /* nothing more to do */ + } + else /* misaligned */ + { + /* + Find an aligned spot inside chunk. + Since we need to give back leading space in a chunk of at + least MINSIZE, if the first calculation places us at + a spot with less than MINSIZE leader, we can move to the + next aligned spot -- we've allocated enough total room so that + this is always possible. + */ + + lim = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & + ~(alignment - 1)); + if ((lim - (char*)p) < (long)MINSIZE) lim = lim + alignment; + + newp = (mchunkptr)lim; + leadsize = lim - (char*)p; + newsize = chunksize(p) - leadsize; + +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + { + newp->prev_size = p->prev_size + leadsize; + set_head(newp, newsize|IS_MMAPPED); + init_malloced_chunk(newp, bytes); + return chunk2mem(newp); + } +#endif + + /* give back leader, use the rest */ + + set_head(newp, newsize | PREV_INUSE); + set_inuse_bit_at_offset(newp, newsize); + set_head_size(p, leadsize); + init_freeable_chunk(p); + fREe(chunk2mem(p)); + p = newp; + + assert (newsize >= nb && (((unsigned long)(chunk2mem(p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + + remainder_size = chunksize(p) - nb; + + if (remainder_size >= (long)MINSIZE) + { + remainder = chunk_at_offset(p, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_head_size(p, nb); + init_freeable_chunk(remainder); + fREe(chunk2mem(remainder)); + } + + init_malloced_chunk(p, bytes); + check_inuse_chunk(p); + return chunk2mem(p); + +} + + + + +/* + valloc just invokes memalign with alignment argument equal + to the page size of the system (or as near to this as can + be figured out from all the includes/defines above.) +*/ + +#if __STD_C +Void_t* vALLOc(size_t bytes) +#else +Void_t* vALLOc(bytes) size_t bytes; +#endif +{ + return mEMALIGn (malloc_getpagesize, bytes); +} + +/* + pvalloc just invokes valloc for the nearest pagesize + that will accommodate request +*/ + + +#if __STD_C +Void_t* pvALLOc(size_t bytes) +#else +Void_t* pvALLOc(bytes) size_t bytes; +#endif +{ + size_t pagesize = malloc_getpagesize; + return mEMALIGn (pagesize, (bytes + pagesize - 1) & ~(pagesize - 1)); +} + +/* + + calloc calls malloc, then zeroes out the allocated chunk. + +*/ + +#if __STD_C +Void_t* cALLOc(size_t n, size_t elem_size) +#else +Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size; +#endif +{ + mchunkptr p; + INTERNAL_SIZE_T csz; + + INTERNAL_SIZE_T sz = n * elem_size; + + /* check if expand_top called, in which case don't need to clear */ +#if MORECORE_CLEARS + mchunkptr oldtop = top; + INTERNAL_SIZE_T oldtopsize = chunksize(top); +#endif + Void_t* mem = mALLOc (sz); + + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + + /* Two optional cases in which clearing not necessary */ + + +#if HAVE_MMAP + if (chunk_is_mmapped(p)) return mem; +#endif + + csz = chunksize(p); + +#if MORECORE_CLEARS + if (p == oldtop && csz > oldtopsize) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif + + malloc_ZERO(mem, csz - OVERHEAD); + /* reinstate moat fill in pad region */ + init_realloced_chunk(p, sz, chunksize(p)); + return mem; + } +} + + + +/* + + Malloc_trim gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + +*/ + +#if __STD_C +int dlmalloc_trim(size_t pad) +#else +int malloc_trim(pad) size_t pad; +#endif +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + char* current_lim; /* address returned by pre-check sbrk call */ + char* new_lim; /* address returned by negative sbrk call */ + + unsigned long pagesz = malloc_getpagesize; + + top_size = chunksize(top); + extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz; + + if (extra < (long)pagesz) /* Not enough memory to release */ + return 0; + + else + { +#ifdef OTHER_SBRKS + /* Test to make sure no one else called sbrk */ + current_lim = (char*)(MORECORE (0)); + if (current_lim != (char*)(top) + top_size) + return 0; /* Apparently we don't own memory; must fail */ + + else +#endif + { + new_lim = (char*)(MORECORE (-extra)); + + if (new_lim == (char*)(MORECORE_FAILURE)) /* sbrk failed? */ + { + /* Try to figure out what we have */ + current_lim = (char*)(MORECORE (0)); + top_size = current_lim - (char*)top; + if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */ + { + sbrked_mem = current_lim - sbrk_base; + set_head(top, top_size | PREV_INUSE); + init_freed_chunk(top, top_size, 0); + } + check_chunk(top); + return 0; + } + + else + { + /* Success. Adjust top accordingly. */ + set_head(top, (top_size - extra) | PREV_INUSE); + sbrked_mem -= extra; + init_freed_chunk(top, top_size - extra, 0); + check_chunk(top); + return 1; + } + } + } +} + + + +/* + malloc_usable_size: + + This routine tells you how many bytes you can actually use in an + allocated chunk, which may be more than you requested (although + often not). You can use this many bytes without worrying about + overwriting other allocated objects. Not a particularly great + programming practice, but still sometimes useful. + +*/ + +#if __STD_C +size_t dlmalloc_usable_size(Void_t* mem) +#else +size_t malloc_usable_size(mem) Void_t* mem; +#endif +{ + mchunkptr p; + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + check_inuse_chunk(p); + maximize_chunk(p); + if(!chunk_is_mmapped(p)) + { + if (!inuse(p)) return 0; + return chunksize(p) - OVERHEAD; + } + return chunksize(p) - OVERHEAD - MMAP_EXTRA; + } +} + + + + +/* Utility to update current_mallinfo for malloc_stats and mallinfo() */ + +static void malloc_update_mallinfo(void) +{ + int i; + mbinptr b; + mchunkptr p; +#if DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = avail >= MINSIZE ? 1 : 0; + check_freefill(top, avail, avail); + +#if DEBUG + if (lowest_chunk) + for (p = lowest_chunk; + p < top && inuse(p) && chunksize(p) >= MINSIZE; + p = next_chunk(p)) + check_inuse_chunk(p); +#endif + + for (i = 1; i < NAV; ++i) + { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) + { +#if DEBUG + check_free_chunk(p); + check_freefill(p, chunksize(p), chunksize(p)); + for (q = next_chunk(p); + q < top && inuse(q) && chunksize(q) >= MINSIZE; + q = next_chunk(q)) + check_inuse_chunk(q); +#endif + avail += chunksize(p); + navail++; + } + } + + current_mallinfo.ordblks = navail; + current_mallinfo.uordblks = sbrked_mem - avail; + current_mallinfo.fordblks = avail; + current_mallinfo.hblks = n_mmaps; + current_mallinfo.hblkhd = mmapped_mem; + current_mallinfo.keepcost = chunksize(top); + +} + + + +/* + + malloc_stats: + + Prints on stderr the amount of space obtain from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), the maximum + number of simultaneous mmap regions used, and the current number + of bytes allocated via malloc (or realloc, etc) but not yet + freed. (Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead.) + +*/ + +void dlmalloc_stats(void) +{ + malloc_update_mallinfo(); + fprintf(stderr, "max system bytes = %10u\n", + (unsigned int)(max_total_mem)); + fprintf(stderr, "system bytes = %10u\n", + (unsigned int)(sbrked_mem + mmapped_mem)); + fprintf(stderr, "in use bytes = %10u\n", + (unsigned int)(current_mallinfo.uordblks + mmapped_mem)); +#if HAVE_MMAP + fprintf(stderr, "max mmap regions = %10u\n", + (unsigned int)max_n_mmaps); +#endif +} + +/* + mallinfo returns a copy of updated current mallinfo. +*/ + +struct mallinfo mALLINFo(void) +{ + malloc_update_mallinfo(); + return current_mallinfo; +} + + + + +/* + mallopt: + + mallopt is the general SVID/XPG interface to tunable parameters. + The format is to provide a (parameter-number, parameter-value) pair. + mallopt then sets the corresponding parameter to the argument + value if it can (i.e., so long as the value is meaningful), + and returns 1 if successful else 0. + + See descriptions of tunable parameters above. + +*/ + +#if __STD_C +int mALLOPt(int param_number, int value) +#else +int mALLOPt(param_number, value) int param_number; int value; +#endif +{ + switch(param_number) + { + case M_TRIM_THRESHOLD: + trim_threshold = value; return 1; + case M_TOP_PAD: + top_pad = value; return 1; + case M_MMAP_THRESHOLD: + mmap_threshold = value; return 1; + case M_MMAP_MAX: +#if HAVE_MMAP + n_mmaps_max = value; return 1; +#else + if (value != 0) return 0; else n_mmaps_max = value; return 1; +#endif + case M_SCANHEAP: +#ifdef DEBUG2 + scanheap = value; +#endif + return 1; + + default: + return 0; + } +} + +/* + +History: + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/winsup/cygwin/dlmalloc.h b/winsup/cygwin/dlmalloc.h new file mode 100644 index 00000000000..b8d7ebeeb48 --- /dev/null +++ b/winsup/cygwin/dlmalloc.h @@ -0,0 +1,93 @@ + +/* + * Header file for BBCized version of Doug Lea's malloc.c, automatically + * generated by + * /source/prod/libbbc/compat/dlmalloc/cvt + * from + * /source/prod/libbbc/compat/dlmalloc/malloc.c + * + * bbclabel: autogenerated + */ +#define _INCLUDE_MALLOC_H_ 1 +void malloc_outofmem(void (*)(void)); + + +struct mallinfo { + int arena; /* total space allocated from system */ + int ordblks; /* number of non-inuse chunks */ + int smblks; /* unused -- always zero */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* total space in mmapped regions */ + int usmblks; /* unused -- always zero */ + int fsmblks; /* unused -- always zero */ + int uordblks; /* total allocated space */ + int fordblks; /* total non-inuse space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + + +#define M_MXFAST 1 /* UNUSED in this malloc */ +#define M_NLBLKS 2 /* UNUSED in this malloc */ +#define M_GRAIN 3 /* UNUSED in this malloc */ +#define M_KEEP 4 /* UNUSED in this malloc */ + + +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 +#define M_SCANHEAP -5 +#define M_FILL + +#ifdef MALLOC_DEBUG + +#define dmalloc(size) malloc_dbg(size, __FILE__, __LINE__) +#define dfree(p) free_dbg(p, __FILE__, __LINE__) +#define drealloc(p, size) realloc_dbg(p, size, __FILE__, __LINE__) +#define dcalloc(n, size) calloc_dbg(n, size, __FILE__, __LINE__) +#define dmemalign(align, size) memalign_dbg(align, size, __FILE__, __LINE__) +#define dvalloc(size) valloc_dbg(size, __FILE__, __LINE__) +#define dpvalloc(size) pvalloc_dbg(size, __FILE__, __LINE__) +#define dmalloc_trim(pad) malloc_trim_dbg(pad, __FILE__, __LINE__) +#define dmalloc_usable_size(p) malloc_usable_size_dbg(p, __FILE__, __LINE__) +#define dmalloc_stats() malloc_stats_dbg(__FILE__, __LINE__) +#define dmallopt(flag, val) mallopt_dbg(flag, val, __FILE__, __LINE__) +#define dmallinfo() mallinfo_dbg(__FILE__, __LINE__) + + +#ifdef __cplusplus +extern "C" { +#endif +void* malloc_dbg(size_t, const char *, int); +void free_dbg(void*, const char *, int); +void* realloc_dbg(void*, size_t, const char *, int); +void* calloc_dbg(size_t, size_t, const char *, int); +void* memalign_dbg(size_t, size_t, const char *, int); +void* valloc_dbg(size_t, const char *, int); +void* pvalloc_dbg(size_t, const char *, int); +int malloc_trim_dbg(size_t, const char *, int); +size_t malloc_usable_size_dbg(void*, const char *, int); +void malloc_stats_dbg(const char *, int); +int mallopt_dbg(int, int, const char *, int); +struct mallinfo mallinfo_dbg(const char *, int); +#ifdef __cplusplus +} +#endif + +#endif /* MALLOC_DEBUG */ + +#ifndef MALLOC_DEBUG + +void* malloc(size_t); +void free(void*); +void* realloc(void*, size_t); +void* calloc(size_t, size_t); +void* memalign(size_t, size_t); +void* valloc(size_t); +void* pvalloc(size_t); +int malloc_trim(size_t); +size_t malloc_usable_size(void*); +void malloc_stats(void); +int mallopt(int, int); +struct mallinfo mallinfo(void); +#endif /* !MALLOC_DEBUG */ diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 1732165256b..c63ed20584e 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -731,18 +731,6 @@ signal_fixup_after_fork () sigproc_init (); } -void __stdcall -signal_fixup_after_exec () -{ - /* Set up child's signal handlers */ - for (int i = 0; i < NSIG; i++) - { - myself->getsig (i).sa_mask = 0; - if (myself->getsig (i).sa_handler != SIG_IGN) - myself->getsig (i).sa_handler = SIG_DFL; - } -} - static int interrupt_on_return (sigthread *, int, void *, struct sigaction&) __attribute__((regparm(3))); static int interrupt_on_return (sigthread *th, int sig, void *handler, struct sigaction& siga) @@ -785,10 +773,9 @@ setup_handler (int sig, void *handler, struct sigaction& siga) CONTEXT cx; bool interrupted = false; sigthread *th = NULL; // Initialization needed to shut up gcc - int prio = INFINITE; if (sigsave.sig) - goto set_pending; + goto out; for (int i = 0; i < CALL_HANDLER_RETRY; i++) { @@ -823,7 +810,18 @@ setup_handler (int sig, void *handler, struct sigaction& siga) SuspendThread on itself then just queue the signal. */ EnterCriticalSection (&mainthread.lock); +#ifndef DEBUGGING sigproc_printf ("suspending mainthread"); +#else + cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + if (!GetThreadContext (hth, &cx)) + memset (&cx, 0, sizeof cx); +#if 0 + if ((cx.Eip & 0xff000000) == 0x77000000) + try_to_debug (); +#endif + sigproc_printf ("suspending mainthread PC %p", cx.Eip); +#endif res = SuspendThread (hth); /* Just release the lock now since we hav suspended the main thread and it definitely can't be grabbing it now. This will have to change, of course, @@ -866,13 +864,6 @@ setup_handler (int sig, void *handler, struct sigaction& siga) } } - if ((DWORD) prio != INFINITE) - { - /* Reset the priority so we can finish this off quickly. */ - SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); - prio = INFINITE; - } - if (th) { interrupted = interrupt_on_return (th, sig, handler, siga); @@ -888,20 +879,11 @@ setup_handler (int sig, void *handler, struct sigaction& siga) if (interrupted) break; - if ((DWORD) prio != INFINITE && !mainthread.frame) - prio = low_priority_sleep (SLEEP_0_STAY_LOW); sigproc_printf ("couldn't interrupt. trying again."); } - set_pending: - if (interrupted) - { - if ((DWORD) prio != INFINITE) - SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); - sigproc_printf ("signal successfully delivered"); - } - - sigproc_printf ("returning %d", interrupted); +out: + sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not "); return interrupted; } #endif /* i386 */ @@ -1225,7 +1207,9 @@ _sigreturn: \n\ cmpl $0,%4 # Did a signal come in? \n\ jz 1f # No, if zero \n\ movl %2,%%eax \n\ - movl %%eax,36(%%esp) # Restore return address \n\ + movl %8,%%ebx # Where return address lives \n\ + movl %%eax,(%%ebx) # Restore return address of \n\ + # most recent caller \n\ jmp 3f \n\ \n\ 1: popl %%eax # saved errno \n\ @@ -1272,10 +1256,10 @@ _sigdelayed0: \n\ popl %%eax \n\ jmp *%%eax \n\ __no_sig_end: \n\ -" : "=m" (sigsave.sig): "X" ((char *) &_impure_ptr->_errno), - "g" (sigsave.retaddr), "g" (sigsave.oldmask), "g" (sigsave.sig), - "g" (sigsave.func), "g" (sigsave.saved_errno), "g" (sigsave.newmask), - "g" (sigsave.retaddr_on_stack) +" : "=m" (sigsave.sig)/*0*/: "X" ((char *) &_impure_ptr->_errno)/*1*/, + "g" (sigsave.retaddr)/*2*/, "g" (sigsave.oldmask)/*3*/, "g" (sigsave.sig)/*4*/, + "g" (sigsave.func)/*5*/, "g" (sigsave.saved_errno)/*6*/, "g" (sigsave.newmask)/*7*/, + "g" (sigsave.retaddr_on_stack)/*8*/ ); } } diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index f9b822522b9..9ecc9edda5c 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -213,7 +213,7 @@ fhandler_base::raw_read (void *ptr, size_t& ulen) int prio = 0; /* ditto */ DWORD len = ulen; - (ssize_t) ulen = -1; + ulen = (size_t) -1; if (read_state) { h = GetCurrentThread (); diff --git a/winsup/cygwin/heap.cc b/winsup/cygwin/heap.cc index 428399320bb..60cbb4fe134 100644 --- a/winsup/cygwin/heap.cc +++ b/winsup/cygwin/heap.cc @@ -139,7 +139,6 @@ sbrk (int n) /* Couldn't allocate memory. Maybe we can reserve some more. Reserve either the maximum of the standard cygwin_shared->heap_chunk_size () or the requested amount. Then attempt to actually allocate it. */ - if ((newbrksize = cygheap->user_heap.chunk) < commitbytes) newbrksize = commitbytes; @@ -147,7 +146,7 @@ sbrk (int n) || VirtualAlloc (cygheap->user_heap.top, newbrksize = commitbytes, MEM_RESERVE, PAGE_NOACCESS)) && VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL) { - (char *) cygheap->user_heap.max += newbrksize; + (char *) cygheap->user_heap.max += pround (newbrksize); goto good; } diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 8a6354ddcad..b99b0640421 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -42,7 +42,7 @@ details. */ changes to the DLL and is mainly informative in nature. */ #define CYGWIN_VERSION_DLL_MAJOR 1005 -#define CYGWIN_VERSION_DLL_MINOR 3 +#define CYGWIN_VERSION_DLL_MINOR 4 /* Major numbers before CYGWIN_VERSION_DLL_EPOCH are incompatible. */ diff --git a/winsup/cygwin/include/sys/cygwin.h b/winsup/cygwin/include/sys/cygwin.h index dccff206862..e117175e0c2 100644 --- a/winsup/cygwin/include/sys/cygwin.h +++ b/winsup/cygwin/include/sys/cygwin.h @@ -92,7 +92,7 @@ enum PID_SPLIT_HEAP = 0x0100, /* Set if the heap has been split, */ /* which means we can't fork again. */ PID_MYSELF = 0x0200, /* Flag that pid is me. */ - PID_UNUSED1 = 0x0400, /* Set if process uses Winsock. */ + PID_NOCLDSTOP = 0x0400, /* Set if no SIGCHLD signal on stop. */ PID_INITIALIZING = 0x0800, /* Set until ready to receive signals. */ PID_USETTY = 0x1000, /* Setting this enables or disables cygwin's */ /* tty support. This is inherited by */ @@ -207,6 +207,7 @@ extern void cygwin_set_impersonation_token (const HANDLE); extern int cygwin32_attach_handle_to_fd (char *, int, HANDLE, mode_t, DWORD); extern int cygwin_attach_handle_to_fd (char *, int, HANDLE, mode_t, DWORD); +#ifdef __CYGWIN__ #include <sys/resource.h> #define TTY_CONSOLE 0x40000000 @@ -250,11 +251,10 @@ struct external_pinfo __uid32_t uid32; __gid32_t gid32; }; - +#endif /*__CYGWIN__*/ #endif /*WINVER*/ #ifdef __cplusplus }; #endif - #endif /* _SYS_CYGWIN_H */ diff --git a/winsup/cygwin/libc/fnmatch.c b/winsup/cygwin/libc/fnmatch.c new file mode 100644 index 00000000000..b48d9e00ab4 --- /dev/null +++ b/winsup/cygwin/libc/fnmatch.c @@ -0,0 +1,220 @@ +/* $OpenBSD: fnmatch.c,v 1.7 2000/03/23 19:13:51 millert Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; +#else +static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.7 2000/03/23 19:13:51 millert Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + */ + +/* Just this single line added for Cygwin. */ +#include "winsup.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <fnmatch.h> + +#define EOS '\0' + +#define RANGE_MATCH 1 +#define RANGE_NOMATCH 0 +#define RANGE_ERROR (-1) + +static int rangematch __P((const char *, char, int, char **)); + +int +fnmatch(const char *pattern, const char *string, int flags) +{ + const char *stringstart; + char *newp; + char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) { + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + } else if (c == '/' && (flags & FNM_PATHNAME)) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = *string) != EOS) { + if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && (flags & FNM_PATHNAME)) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + switch (rangematch(pattern, *string, flags, &newp)) { + case RANGE_ERROR: + /* not a good range, treat as normal text */ + goto normal; + case RANGE_MATCH: + pattern = newp; + break; + case RANGE_NOMATCH: + return (FNM_NOMATCH); + } + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = *pattern++) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + normal: + if (c != *string && !((flags & FNM_CASEFOLD) && + (tolower((unsigned char)c) == + tolower((unsigned char)*string)))) + return (FNM_NOMATCH); + ++string; + break; + } + /* NOTREACHED */ +} + +static int +rangematch(const char *pattern, char test, int flags, char **newp) +{ + int negate, ok; + char c, c2; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ((negate = (*pattern == '!' || *pattern == '^'))) + ++pattern; + + if (flags & FNM_CASEFOLD) + test = tolower((unsigned char)test); + + /* + * A right bracket shall lose its special meaning and represent + * itself in a bracket expression if it occurs first in the list. + * -- POSIX.2 2.8.3.2 + */ + ok = 0; + c = *pattern++; + do { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = *pattern++; + if (c == EOS) + return (RANGE_ERROR); + if (c == '/' && (flags & FNM_PATHNAME)) + return (RANGE_NOMATCH); + if ((flags & FNM_CASEFOLD)) + c = tolower((unsigned char)c); + if (*pattern == '-' + && (c2 = *(pattern+1)) != EOS && c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = *pattern++; + if (c2 == EOS) + return (RANGE_ERROR); + if (flags & FNM_CASEFOLD) + c2 = tolower((unsigned char)c2); + if (c <= test && test <= c2) + ok = 1; + } else if (c == test) + ok = 1; + } while ((c = *pattern++) != ']'); + + *newp = (char *)pattern; + return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); +} diff --git a/winsup/cygwin/malloc.cc b/winsup/cygwin/malloc.cc index 383a590c890..7c85bd68102 100644 --- a/winsup/cygwin/malloc.cc +++ b/winsup/cygwin/malloc.cc @@ -3744,6 +3744,7 @@ void fREe(mem) Void_t* mem; mchunkptr bck; /* misc temp for linking */ mchunkptr fwd; /* misc temp for linking */ + check_malloc_state(); /* free(0) has no effect */ if (mem != 0) { p = mem2chunk(mem); @@ -3878,6 +3879,7 @@ void fREe(mem) Void_t* mem; #endif } } + check_malloc_state(); } /* diff --git a/winsup/cygwin/miscfuncs.cc b/winsup/cygwin/miscfuncs.cc index fea1b12498b..d9e4a7adb15 100644 --- a/winsup/cygwin/miscfuncs.cc +++ b/winsup/cygwin/miscfuncs.cc @@ -8,6 +8,7 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ +#define _WIN32_WINNT 0x400 #include "winsup.h" #include "cygerrno.h" #include <sys/errno.h> @@ -16,6 +17,7 @@ details. */ #include <limits.h> #include <winbase.h> #include <winnls.h> +#include "cygthread.h" long tls_ix = -1; @@ -306,13 +308,27 @@ low_priority_sleep (DWORD secs) staylow = true; } - int main_prio = GetThreadPriority (hMainThread); - if (curr_prio != main_prio) - /* Force any threads in normal priority to be scheduled */ - SetThreadPriority (thisthread, main_prio); - Sleep (secs); + if (!secs && wincap.has_switch_to_thread ()) + { + for (int i = 0; i < 10; i++) + SwitchToThread (); + } + else + { + int new_prio; + if (GetCurrentThreadId () == cygthread::main_thread_id) + new_prio = THREAD_PRIORITY_LOWEST; + else + new_prio = GetThreadPriority (hMainThread); + + if (curr_prio != new_prio) + /* Force any threads in normal priority to be scheduled */ + SetThreadPriority (thisthread, new_prio); + Sleep (secs); + + if (!staylow || curr_prio == new_prio) + SetThreadPriority (thisthread, curr_prio); + } - if (!staylow || curr_prio == main_prio) - SetThreadPriority (thisthread, curr_prio); return curr_prio; } diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index 6c77e501540..9d7cc00fdb2 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -25,6 +25,7 @@ details. */ #include <netdb.h> #define USE_SYS_TYPES_FD_SET #include <winsock2.h> +#include <assert.h> #include "cygerrno.h" #include "security.h" #include "path.h" @@ -381,95 +382,205 @@ set_host_errno () h_errno = NETDB_INTERNAL; } -static void -free_char_list (char **clist) +inline int +DWORD_round (int n) { - if (clist) - { - for (char **cl = clist; *cl; ++cl) - free (*cl); - free (clist); - } + return sizeof (DWORD) * (((n + sizeof (DWORD) - 1)) / sizeof (DWORD)); } -static char ** -dup_char_list (char **src) +inline int +strlen_round (const char *s) { - char **dst; - int cnt = 0; - - for (char **cl = src; *cl; ++cl) - ++cnt; - if (!(dst = (char **) calloc (cnt + 1, sizeof *dst))) - return NULL; - while (cnt-- > 0) - if (!(dst[cnt] = strdup (src[cnt]))) - return NULL; - return dst; + if (!s) + return 0; + return DWORD_round (strlen (s) + 1); } -#define free_addr_list(addr_list) free_char_list (addr_list) +#pragma pack(push,2) +struct pservent +{ + char *s_name; + char **s_aliases; + short s_port; + char *s_proto; +}; +#pragma pack(pop) -static char ** -dup_addr_list (char **src, unsigned int size) +struct unionent { - char **dst; - int cnt = 0; + char *name; + char **list; + short port_proto_addrtype; + short h_len; + union + { + char *s_proto; + char **h_addr_list; + }; +}; - for (char **cl = src; *cl; ++cl) - ++cnt; - if (!(dst = (char **) calloc (cnt + 1, sizeof *dst))) - return NULL; - while (cnt-- > 0) - { - if (!(dst[cnt] = (char *) malloc (size))) - return NULL; - memcpy (dst[cnt], src[cnt], size); - } - return dst; -} +enum struct_type +{ + is_hostent, is_protoent, is_servent +}; -static void -free_protoent_ptr (struct protoent *&p) +static const char *entnames[] = {"host", "proto", "serv"}; + +/* Generic "dup a {host,proto,serv}ent structure" function. + This is complicated because we need to be able to free the + structure at any point and we can't rely on the pointer contents + being untouched by callers. So, we allocate a chunk of memory + large enough to hold the structure and all of the stuff it points + to then we copy the source into this new block of memory. + The 'unionent' struct is a union of all of the currently used + *ent structure. */ + +#ifdef DEBUGGING +static void * +#else +static inline void * +#endif +dup_ent (void *old, void *src0, struct_type type) { - if (p) + if (old) { - debug_printf ("protoent: %s", p->p_name); - - if (p->p_name) - free (p->p_name); - free_char_list (p->p_aliases); - free ((void *) p); - p = NULL; + debug_printf ("freeing old %sent structure \"%s\" %p\n", entnames[type], + ((unionent *) old)->name, old); + free (old); } -} -static struct protoent * -dup_protoent_ptr (struct protoent *src) -{ - if (!src) + if (!src0) return NULL; - struct protoent *dst = (struct protoent *) calloc (1, sizeof *dst); + unionent *src = (unionent *) src0; + debug_printf ("duping %sent \"%s\", %p", entnames[type], + src ? src->name : "<null!>", src); - if (!dst) - return NULL; + /* Find the size of the raw structure minus any character strings, etc. */ + int sz, struct_sz; + switch (type) + { + case is_protoent: + struct_sz = sizeof (protoent); + break; + case is_servent: + struct_sz = sizeof (servent); + break; + case is_hostent: + struct_sz = sizeof (hostent); + break; + default: + api_fatal ("called with invalid value %d", type); + break; + } - debug_printf ("protoent: %s", src->p_name); + /* Every *ent begins with a name. Calculate it's length. */ + int namelen = strlen_round (src->name); + sz = struct_sz + namelen; - dst->p_proto = src->p_proto; - if (src->p_name && !(dst->p_name = strdup (src->p_name))) - goto out; - if (src->p_aliases && !(dst->p_aliases = dup_char_list (src->p_aliases))) - goto out; + char **av; + /* The next field in every *ent is an argv list of "something". + Calculate the number of components and how much space the + character strings will take. */ + int list_len = 0; + for (av = src->list; av && *av; av++) + { + list_len++; + sz += sizeof (char **) + strlen_round (*av); + } - debug_printf ("protoent: copied %s", dst->p_name); + /* NULL terminate if there actually was a list */ + if (av) + { + sz += sizeof (char **); + list_len++; + } - return dst; + /* Do servent/hostent specific processing */ + int protolen = 0; + int addr_list_len = 0; + if (type == is_servent) + sz += (protolen = strlen_round (src->s_proto)); + else if (type == is_hostent) + { + /* Calculate the length and storage used for h_addr_list */ + for (av = src->h_addr_list; av && *av; av++) + { + addr_list_len++; + sz += sizeof (char **) + DWORD_round (src->h_len); + } + if (av) + { + sz += sizeof (char **); + addr_list_len++; + } + } -out: - free_protoent_ptr (dst); - return NULL; + /* Allocate the storage needed */ + unionent *dst = (unionent *) calloc (1, sz); + + /* Hopefully, this worked. */ + if (dst) + { + /* This field is common to all *ent structures but named differently + in each, of course. */ + dst->port_proto_addrtype = src->port_proto_addrtype; + + /* Copy the name field to dst, using space just beyond the end of + the dst structure. */ + char *dp = ((char *) dst) + struct_sz; + strcpy (dst->name = dp, src->name); + dp += namelen; + + /* Copy the 'list' type to dst, using space beyond end of structure + + storage for name. */ + if (src->list) + { + char **dav = dst->list = (char **) dp; + dp += sizeof (char **) * list_len; + for (av = src->list; av && *av; av++) + { + int len = strlen (*av) + 1; + memcpy (*dav++ = dp, *av, len); + dp += DWORD_round (len); + } + } + + /* Do servent/hostent specific processing. */ + if (type == is_servent) + { + if (src->s_proto) + { + char *s_proto; + /* Windows 95 idiocy. Structure is misaligned on Windows 95. + Kludge around this by trying a different pointer alignment. */ + if (IsBadReadPtr (src->s_proto, sizeof (src->s_proto)) + && !IsBadReadPtr (((pservent *) src)->s_proto, sizeof (src->s_proto))) + s_proto = ((pservent *) src)->s_proto; + else + s_proto = src->s_proto; + strcpy (dst->s_proto = dp, s_proto); + dp += protolen; + } + } + else if (type == is_hostent) + { + /* Transfer h_len and duplicate contents of h_addr_list, using + memory after 'list' allocation. */ + dst->h_len = src->h_len; + char **dav = dst->h_addr_list = (char **) dp; + dp += sizeof (char **) * addr_list_len; + for (av = src->h_addr_list; av && *av; av++) + { + memcpy (*dav++ = dp, *av, src->h_len); + dp += DWORD_round (src->h_len); + } + } + /* Sanity check that we did our bookkeeping correctly. */ + assert ((dp - (char *) dst) == sz); + } + debug_printf ("duped %sent \"%s\", %p", entnames[type], dst ? dst->name : "<null!>", dst); + return dst; } #ifdef _MT_SAFE @@ -484,8 +595,8 @@ cygwin_getprotobyname (const char *p) { if (check_null_str_errno (p)) return NULL; - free_protoent_ptr (protoent_buf); - protoent_buf = dup_protoent_ptr (getprotobyname (p)); + protoent_buf = (protoent *) dup_ent (protoent_buf, getprotobyname (p), + is_protoent); if (!protoent_buf) set_winsock_errno (); @@ -497,8 +608,8 @@ cygwin_getprotobyname (const char *p) extern "C" struct protoent * cygwin_getprotobynumber (int number) { - free_protoent_ptr (protoent_buf); - protoent_buf = dup_protoent_ptr (getprotobynumber (number)); + protoent_buf = (protoent *) dup_ent (protoent_buf, getprotobynumber (number), + is_protoent); if (!protoent_buf) set_winsock_errno (); @@ -824,71 +935,6 @@ cygwin_connect (int fd, const struct sockaddr *name, int namelen) return res; } -static void -free_servent_ptr (struct servent *&p) -{ - if (p) - { - debug_printf ("servent: %s", p->s_name); - - if (p->s_name) - free (p->s_name); - if (p->s_proto) - free (p->s_proto); - free_char_list (p->s_aliases); - free ((void *) p); - p = NULL; - } -} - -#pragma pack(push,2) -struct pservent -{ - char *s_name; - char **s_aliases; - short s_port; - char *s_proto; -}; - -#pragma pack(pop) -static struct servent * -dup_servent_ptr (struct servent *src) -{ - if (!src) - return NULL; - - struct servent *dst = (struct servent *) calloc (1, sizeof *dst); - - if (!dst) - return NULL; - - debug_printf ("servent: %s", src->s_name); - - dst->s_port = src->s_port; - if (src->s_name && !(dst->s_name = strdup (src->s_name))) - goto out; - if (src->s_aliases && !(dst->s_aliases = dup_char_list (src->s_aliases))) - goto out; - char *s_proto; - - if (IsBadReadPtr (src->s_proto, sizeof (src->s_proto)) - && !IsBadReadPtr (((pservent *) src)->s_proto, sizeof (src->s_proto))) - s_proto = ((pservent *) src)->s_proto; - else - s_proto = src->s_proto; - - if (s_proto && !(dst->s_proto = strdup (s_proto))) - goto out; - - debug_printf ("servent: copied %s", dst->s_name); - - return dst; - -out: - free_servent_ptr (dst); - return NULL; -} - #ifdef _MT_SAFE #define servent_buf _reent_winsup ()->_servent_buf #else @@ -905,8 +951,8 @@ cygwin_getservbyname (const char *name, const char *proto) || (proto != NULL && check_null_str_errno (proto))) return NULL; - free_servent_ptr (servent_buf); - servent_buf = dup_servent_ptr (getservbyname (name, proto)); + servent_buf = (servent *) dup_ent (servent_buf, getservbyname (name, proto), + is_servent); if (!servent_buf) set_winsock_errno (); @@ -923,8 +969,8 @@ cygwin_getservbyport (int port, const char *proto) if (proto != NULL && check_null_str_errno (proto)) return NULL; - free_servent_ptr (servent_buf); - servent_buf = dup_servent_ptr (getservbyport (port, proto)); + servent_buf = (servent *) dup_ent (servent_buf, getservbyport (port, proto), + is_servent); if (!servent_buf) set_winsock_errno (); @@ -955,54 +1001,6 @@ cygwin_gethostname (char *name, size_t len) return 0; } -static void -free_hostent_ptr (struct hostent *&p) -{ - if (p) - { - debug_printf ("hostent: %s", p->h_name); - - if (p->h_name) - free ((void *) p->h_name); - free_char_list (p->h_aliases); - free_addr_list (p->h_addr_list); - free ((void *) p); - p = NULL; - } -} - -static struct hostent * -dup_hostent_ptr (struct hostent *src) -{ - if (!src) - return NULL; - - struct hostent *dst = (struct hostent *) calloc (1, sizeof *dst); - - if (!dst) - return NULL; - - debug_printf ("hostent: %s", src->h_name); - - dst->h_addrtype = src->h_addrtype; - dst->h_length = src->h_length; - if (src->h_name && !(dst->h_name = strdup (src->h_name))) - goto out; - if (src->h_aliases && !(dst->h_aliases = dup_char_list (src->h_aliases))) - goto out; - if (src->h_addr_list - && !(dst->h_addr_list = dup_addr_list (src->h_addr_list, src->h_length))) - goto out; - - debug_printf ("hostent: copied %s", dst->h_name); - - return dst; - -out: - free_hostent_ptr (dst); - return NULL; -} - #ifdef _MT_SAFE #define hostent_buf _reent_winsup ()->_hostent_buf #else @@ -1041,8 +1039,8 @@ cygwin_gethostbyname (const char *name) return &tmp; } - free_hostent_ptr (hostent_buf); - hostent_buf = dup_hostent_ptr (gethostbyname (name)); + hostent_buf = (hostent *) dup_ent (hostent_buf, gethostbyname (name), + is_hostent); if (!hostent_buf) { set_winsock_errno (); @@ -1065,8 +1063,9 @@ cygwin_gethostbyaddr (const char *addr, int len, int type) if (__check_invalid_read_ptr_errno (addr, len)) return NULL; - free_hostent_ptr (hostent_buf); - hostent_buf = dup_hostent_ptr (gethostbyaddr (addr, len, type)); + hostent_buf = (hostent *) dup_ent (hostent_buf, + gethostbyaddr (addr, len, type), + is_hostent); if (!hostent_buf) { set_winsock_errno (); diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 5f078726793..ca8f207f77e 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -2360,8 +2360,7 @@ mount (const char *win32_path, const char *posix_path, unsigned flags) { int res = -1; - if (check_null_empty_str_errno (win32_path) - || check_null_empty_str_errno (posix_path)) + if (check_null_empty_str_errno (posix_path)) /* errno set */; else if (strpbrk (posix_path, "\\:")) set_errno (EINVAL); @@ -2373,7 +2372,7 @@ mount (const char *win32_path, const char *posix_path, unsigned flags) res = mount_table->write_cygdrive_info_to_registry (posix_path, flags); win32_path = NULL; } - else + else if (!check_null_empty_str_errno (win32_path)) res = mount_table->add_item (win32_path, posix_path, flags, TRUE); syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags); diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h index 3599015cc60..030c7e91b43 100644 --- a/winsup/cygwin/pinfo.h +++ b/winsup/cygwin/pinfo.h @@ -38,6 +38,8 @@ enum picom PICOM_FIFO = 2 }; +extern struct sigaction *global_sigs; + class _pinfo { public: @@ -112,11 +114,9 @@ public: inline struct sigaction& getsig (int sig) { - return thread2signal ? thread2signal->sigs[sig] : sigs[sig]; + return global_sigs[sig]; } - inline void copysigs (_pinfo *p) {memcpy (sigs, p->sigs, sizeof (sigs));} - inline sigset_t& getsigmask () { return thread2signal ? *thread2signal->sigmask : sig_mask; @@ -145,7 +145,6 @@ public: friend void __stdcall set_myself (pid_t, HANDLE); private: - struct sigaction sigs[NSIG]; sigset_t sig_mask; /* one set for everything to ignore. */ LONG _sigtodo[NSIG + __SIGOFFSET]; pthread *thread2signal; // NULL means thread any other means a pthread diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc index 2fef656e9e4..a7edeace304 100644 --- a/winsup/cygwin/shm.cc +++ b/winsup/cygwin/shm.cc @@ -1,6 +1,6 @@ /* shm.cc: Single unix specification IPC interface for Cygwin. - Copyright 2002 Red Hat, Inc. + Copyright 2002,2003 Red Hat, Inc. Written by Conrad Scott <conrad.scott@dsl.pipex.com>. Based on code by Robert Collins <robert.collins@hotmail.com>. diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index baad718b94f..2b890dd624f 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -338,6 +338,12 @@ sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact) if (newact->sa_handler == SIG_DFL && sig == SIGCHLD) sig_clear (sig); set_sigcatchers (oa.sa_handler, newact->sa_handler); + if (sig == SIGCHLD) + { + myself->process_state &= ~PID_NOCLDSTOP; + if (newact->sa_flags & SA_NOCLDSTOP); + myself->process_state |= PID_NOCLDSTOP; + } } if (oldact) diff --git a/winsup/cygwin/sigproc.cc b/winsup/cygwin/sigproc.cc index f990710fa5d..6ce58fed804 100644 --- a/winsup/cygwin/sigproc.cc +++ b/winsup/cygwin/sigproc.cc @@ -17,6 +17,7 @@ details. */ #include <stdlib.h> #include <sys/cygwin.h> #include <assert.h> +#include <sys/signal.h> #include "cygerrno.h" #include "sync.h" #include "sigproc.h" @@ -48,12 +49,35 @@ details. */ #define NZOMBIES 256 -LONG local_sigtodo[TOTSIGS]; -inline LONG* getlocal_sigtodo (int sig) +static LONG local_sigtodo[TOTSIGS]; +struct sigaction *global_sigs; + +inline LONG * +getlocal_sigtodo (int sig) { return local_sigtodo + __SIGOFFSET + sig; } +void __stdcall +sigalloc () +{ + cygheap->sigs = global_sigs = + (struct sigaction *) ccalloc (HEAP_SIGS, NSIG, sizeof (struct sigaction)); +} + +void __stdcall +signal_fixup_after_exec () +{ + global_sigs = cygheap->sigs; + /* Set up child's signal handlers */ + for (int i = 0; i < NSIG; i++) + { + myself->getsig (i).sa_mask = 0; + if (myself->getsig (i).sa_handler != SIG_IGN) + myself->getsig (i).sa_handler = SIG_DFL; + } +} + /* * Global variables */ @@ -127,9 +151,9 @@ static bool NO_COPY pending_signals = false; // true if signals pending /* Functions */ -static int __stdcall checkstate (waitq *); +static int __stdcall checkstate (waitq *) __attribute__ ((regparm (1))); static __inline__ BOOL get_proc_lock (DWORD, DWORD); -static HANDLE __stdcall getsem (_pinfo *, const char *, int, int); +static HANDLE __stdcall getevent (_pinfo *, const char *) __attribute__ ((regparm (2))); static void __stdcall remove_zombie (int); static DWORD WINAPI wait_sig (VOID *arg); static int __stdcall stopped_or_terminated (waitq *, _pinfo *); @@ -300,7 +324,6 @@ proc_subproc (DWORD what, DWORD val) vchild->sid = myself->sid; vchild->ctty = myself->ctty; vchild->process_state |= PID_INITIALIZING | (myself->process_state & PID_USETTY); - vchild->copysigs (myself); sigproc_printf ("added pid %d to wait list, slot %d, winpid %p, handle %p", vchild->pid, nchildren, vchild->dwProcessId, @@ -540,12 +563,11 @@ sig_dispatch_pending () sigframe thisframe (mainthread); - int was_pending = pending_signals; #ifdef DEBUGGING - sigproc_printf ("pending_signals %d", was_pending); + sigproc_printf ("pending_signals %d", pending_signals); #endif - if (!was_pending) + if (!pending_signals) #ifdef DEBUGGING sigproc_printf ("no need to wake anything up"); #else @@ -559,10 +581,7 @@ sig_dispatch_pending () #endif } - if (was_pending) - thisframe.call_signal_handler (); - - return was_pending; + return thisframe.call_signal_handler (); } /* Message initialization. Called from dll_crt0_1 @@ -679,18 +698,21 @@ sig_send (_pinfo *p, int sig, DWORD ebp, bool exception) sigproc_printf ("pid %d, signal %d, its_me %d", p->pid, sig, its_me); LONG *todo; + bool issem; if (its_me) { if (!wait_for_completion) { thiscatch = sigcatch_nosync; todo = myself->getsigtodo (sig); + issem = false; } else if (tid != mainthread.id) { thiscatch = sigcatch_nonmain; thiscomplete = sigcomplete_nonmain; todo = getlocal_sigtodo (sig); + issem = true; } else { @@ -698,12 +720,16 @@ sig_send (_pinfo *p, int sig, DWORD ebp, bool exception) thiscomplete = sigcomplete_main; thisframe.set (mainthread, ebp, exception); todo = getlocal_sigtodo (sig); + issem = true; } } - else if (thiscatch = getsem (p, "sigcatch", 0, 0)) - todo = p->getsigtodo (sig); + else if ((thiscatch = getevent (p, "sigcatch"))) + { + todo = p->getsigtodo (sig); + issem = false; + } else - goto out; // Couldn't get the semaphore. getsem issued + goto out; // Couldn't get the semaphore. getevent issued // an error, if appropriate. #if WHEN_MULTI_THREAD_SIGNALS_WORK @@ -719,15 +745,7 @@ sig_send (_pinfo *p, int sig, DWORD ebp, bool exception) /* Notify the process that a signal has arrived. */ - SetLastError (0); - -#if 0 - int prio; - prio = GetThreadPriority (GetCurrentThread ()); - (void) SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); -#endif - - if (!ReleaseSemaphore (thiscatch, 1, NULL) && (int) GetLastError () > 0) + if (issem ? !ReleaseSemaphore (thiscatch, 1, NULL) : !SetEvent (thiscatch)) { /* Couldn't signal the semaphore. This probably means that the * process is exiting. @@ -764,24 +782,9 @@ sig_send (_pinfo *p, int sig, DWORD ebp, bool exception) else { sigproc_printf ("Waiting for thiscomplete %p", thiscomplete); - - SetLastError (0); rc = WaitForSingleObject (thiscomplete, WSSC); -#if 0 // STILL NEEDED? - /* Check for strangeness due to this thread being redirected by the - signal handler. Sometimes a WAIT_TIMEOUT will occur when the - thread hasn't really timed out. So, check again. - FIXME: This isn't foolproof. */ - if (rc != WAIT_OBJECT_0 && - WaitForSingleObject (thiscomplete, 0) == WAIT_OBJECT_0) - rc = WAIT_OBJECT_0; -#endif } -#if 0 - SetThreadPriority (GetCurrentThread (), prio); -#endif - if (rc == WAIT_OBJECT_0) rc = 0; // Successful exit else @@ -892,7 +895,7 @@ out: /* Get or create a process specific semaphore used in message passing. */ static HANDLE __stdcall -getsem (_pinfo *p, const char *str, int init, int max) +getevent (_pinfo *p, const char *str) { HANDLE h; char sem_name[MAX_PATH]; @@ -912,14 +915,18 @@ getsem (_pinfo *p, const char *str, int init, int max) low_priority_sleep (1); } - SetLastError (0); if (p == NULL) { char sa_buf[1024]; DWORD winpid = GetCurrentProcessId (); +#if 0 h = CreateSemaphore (sec_user_nih (sa_buf), init, max, str = shared_name (sem_name, str, winpid)); +#else + h = CreateEvent (sec_user_nih (sa_buf), FALSE, FALSE, + str = shared_name (sem_name, str, winpid)); +#endif p = myself; if (!h) { @@ -929,8 +936,13 @@ getsem (_pinfo *p, const char *str, int init, int max) } else { +#if 0 h = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE, shared_name (sem_name, str, p->dwProcessId)); +#else + h = OpenEvent (EVENT_ALL_ACCESS, FALSE, + shared_name (sem_name, str, p->dwProcessId)); +#endif if (!h) { @@ -1075,7 +1087,7 @@ wait_sig (VOID *self) * sigcomplete_nonmain - semaphore signaled for non-main thread on signal * completion */ - sigcatch_nosync = getsem (NULL, "sigcatch", 0, MAXLONG); + sigcatch_nosync = getevent (NULL, "sigcatch"); sigcatch_nonmain = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL); sigcatch_main = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL); sigcomplete_nonmain = CreateSemaphore (&sec_none_nih, 0, MAXLONG, NULL); @@ -1137,8 +1149,7 @@ wait_sig (VOID *self) (void) SetThreadPriority (GetCurrentThread (), WAIT_SIG_PRIORITY); /* sigproc_terminate sets sig_loop_wait to zero to indicate that - * this thread should terminate. - */ + this thread should terminate. */ if (rc == WAIT_TIMEOUT) { if (!sig_loop_wait) @@ -1158,81 +1169,90 @@ wait_sig (VOID *self) sigproc_printf ("awake, rc %d", rc); LONG *todo; if (rc != RC_NOSYNC) - todo = *todos; + todo = todos[0]; else todo = todos[1]; /* A sigcatch semaphore has been signaled. Scan the sigtodo - * array looking for any unprocessed signals. - */ + array looking for any unprocessed signals. */ pending_signals = false; - bool more_signals = false; + unsigned more_signals = 0; bool saw_failed_interrupt = false; do - for (int sig = -__SIGOFFSET, more_signals = false; sig < NSIG; sig++) - { - LONG x = InterlockedDecrement (todo + sig); - if (x < 0) - InterlockedIncrement (todo + sig); - else if (x >= 0) - { - if (sig > 0 && sig != SIGKILL && sig != SIGSTOP && - (sigismember (&myself->getsigmask (), sig) || - main_vfork->pid || - (sig != SIGCONT && ISSTATE (myself, PID_STOPPED)))) - { - sigproc_printf ("signal %d blocked", sig); - pending_signals = true; // FIXME: This will cause unnecessary sig_dispatch_pending spins - InterlockedIncrement (myself->getsigtodo (sig)); - } - else - { - /* Found a signal to process */ - if (rc != RC_NOSYNC) - pending_signals = true; // There should be an armed semaphore, in this case - - sigproc_printf ("processing signal %d", sig); - switch (sig) - { - case __SIGFLUSH: - if (rc == RC_MAIN) - { - flush = true; - ReleaseSemaphore (sigcatch_nosync, 1, NULL); - goto out1; - } - break; - - /* Internal signal to turn on stracing. */ - case __SIGSTRACE: - strace.hello (); - break; - - case __SIGCOMMUNE: - talktome (); - break; - - /* A normal UNIX signal */ - default: - sigproc_printf ("Got signal %d", sig); - if (!sig_handle (sig)) - { - pending_signals = saw_failed_interrupt = true; - sigproc_printf ("couldn't send signal %d", sig); - InterlockedIncrement (myself->getsigtodo (sig)); - } - } - if (rc == RC_NOSYNC) - more_signals = x > 0; - } - - if (sig == SIGCHLD) - proc_subproc (PROC_CLEARWAIT, 0); - if (saw_failed_interrupt || rc != RC_NOSYNC) - goto out; - } - } - while (more_signals); + { + more_signals = 0; + for (int sig = -__SIGOFFSET; sig < NSIG; sig++) + { + LONG x = InterlockedDecrement (todo + sig); + if (x < 0) + InterlockedIncrement (todo + sig); + else if (x >= 0) + { + /* If x > 0, we have to deal with a signal at some later point */ + if (rc != RC_NOSYNC && x > 0) + pending_signals = true; // There should be an armed semaphore, in this case + + if (sig > 0 && sig != SIGKILL && sig != SIGSTOP && + (sigismember (&myself->getsigmask (), sig) || + main_vfork->pid || + (sig != SIGCONT && ISSTATE (myself, PID_STOPPED)))) + { + sigproc_printf ("signal %d blocked", sig); + x = InterlockedIncrement (myself->getsigtodo (sig)); + pending_signals = true; + } + else + { + sigproc_printf ("processing signal %d", sig); + switch (sig) + { + case __SIGFLUSH: + if (rc == RC_MAIN) + { + flush = true; + SetEvent (sigcatch_nosync); + goto out1; + } + break; + + /* Internal signal to turn on stracing. */ + case __SIGSTRACE: + strace.hello (); + break; + + case __SIGCOMMUNE: + talktome (); + break; + + /* A normal UNIX signal */ + default: + sigproc_printf ("Got signal %d", sig); + if (!sig_handle (sig)) + { + saw_failed_interrupt = true; + x = InterlockedIncrement (myself->getsigtodo (sig)); + pending_signals = true; + } + } + if (rc == RC_NOSYNC && x > 0) + more_signals++; + } + + if (sig == SIGCHLD) + proc_subproc (PROC_CLEARWAIT, 0); + + /* Need to take special action if an interrupt failed due to main thread not + getting around to calling handler yet. */ + if (saw_failed_interrupt || rc != RC_NOSYNC) + goto out; + } + } +#ifdef DEBUGGING + if (more_signals > 100) + system_printf ("hmm. infinite loop? more_signals %u\n", more_signals); +#endif + } + while (more_signals && sig_loop_wait); out: /* Signal completion of signal handling depending on which semaphore @@ -1245,10 +1265,11 @@ wait_sig (VOID *self) sigproc_printf ("set main thread completion event"); flush = false; } + out1: if (saw_failed_interrupt) { - ReleaseSemaphore (sigcatch_nosync, 1, NULL); + SetEvent (sigcatch_nosync); low_priority_sleep (0); /* Hopefully, other thread will be waking up soon. */ } sigproc_printf ("looping"); diff --git a/winsup/cygwin/sigproc.h b/winsup/cygwin/sigproc.h index d3061bc504c..360ae8bcba3 100644 --- a/winsup/cygwin/sigproc.h +++ b/winsup/cygwin/sigproc.h @@ -101,12 +101,12 @@ BOOL __stdcall my_parent_is_alive (); extern "C" int __stdcall sig_dispatch_pending (); extern "C" void __stdcall set_process_mask (sigset_t newmask); extern "C" void __stdcall reset_signal_arrived (); -int __stdcall sig_handle (int); -void __stdcall sig_clear (int); -void __stdcall sig_set_pending (int); +int __stdcall sig_handle (int) __attribute__ ((regparm (1))); +void __stdcall sig_clear (int) __attribute__ ((regparm (1))); +void __stdcall sig_set_pending (int) __attribute__ ((regparm (1))); int __stdcall handle_sigsuspend (sigset_t); -int __stdcall proc_subproc (DWORD, DWORD); +int __stdcall proc_subproc (DWORD, DWORD) __attribute__ ((regparm (2))); class _pinfo; void __stdcall proc_terminate (); @@ -120,6 +120,7 @@ int __stdcall sig_send (_pinfo *, int, DWORD ebp = (DWORD) __builtin_frame_addre void __stdcall signal_fixup_after_fork (); void __stdcall signal_fixup_after_exec (); void __stdcall wait_for_sigthread (); +void __stdcall sigalloc (); extern char myself_nowait_dummy[]; extern char myself_nowait_nonmain_dummy[]; diff --git a/winsup/cygwin/speclib b/winsup/cygwin/speclib index cd463f808ae..9de9f50c4c8 100755 --- a/winsup/cygwin/speclib +++ b/winsup/cygwin/speclib @@ -26,7 +26,7 @@ sort -o /tmp/$$.objs -u /tmp/$$.objs [ -s /tmp/$$.objs ] || { echo "speclib: couldn't find symbols for $lib" 1>&2; exit 1; } -/bin/rm -f /tmp/$$>dir +/bin/rm -rf /tmp/$$.dir mkdir /tmp/$$.dir cd /tmp/$$.dir if v; then diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index dbc39132d59..33676ee6f31 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -49,7 +49,8 @@ static NO_COPY wincaps wincap_unknown = { supports_reading_modem_output_lines:false, needs_memory_protection:false, pty_needs_alloc_console:false, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_95 = { @@ -90,7 +91,8 @@ static NO_COPY wincaps wincap_95 = { supports_reading_modem_output_lines:false, needs_memory_protection:false, pty_needs_alloc_console:false, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_95osr2 = { @@ -131,7 +133,8 @@ static NO_COPY wincaps wincap_95osr2 = { supports_reading_modem_output_lines:false, needs_memory_protection:false, pty_needs_alloc_console:false, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_98 = { @@ -172,7 +175,8 @@ static NO_COPY wincaps wincap_98 = { supports_reading_modem_output_lines:false, needs_memory_protection:false, pty_needs_alloc_console:false, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_98se = { @@ -213,7 +217,8 @@ static NO_COPY wincaps wincap_98se = { supports_reading_modem_output_lines:false, needs_memory_protection:false, pty_needs_alloc_console:false, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_me = { @@ -254,7 +259,8 @@ static NO_COPY wincaps wincap_me = { supports_reading_modem_output_lines:false, needs_memory_protection:false, pty_needs_alloc_console:false, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_nt3 = { @@ -295,7 +301,8 @@ static NO_COPY wincaps wincap_nt3 = { supports_reading_modem_output_lines:true, needs_memory_protection:true, pty_needs_alloc_console:true, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:false }; static NO_COPY wincaps wincap_nt4 = { @@ -336,7 +343,8 @@ static NO_COPY wincaps wincap_nt4 = { supports_reading_modem_output_lines:true, needs_memory_protection:true, pty_needs_alloc_console:true, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:true }; static NO_COPY wincaps wincap_nt4sp4 = { @@ -377,7 +385,8 @@ static NO_COPY wincaps wincap_nt4sp4 = { supports_reading_modem_output_lines:true, needs_memory_protection:true, pty_needs_alloc_console:true, - has_terminal_services:false + has_terminal_services:false, + has_switch_to_thread:true }; static NO_COPY wincaps wincap_2000 = { @@ -418,7 +427,8 @@ static NO_COPY wincaps wincap_2000 = { supports_reading_modem_output_lines:true, needs_memory_protection:true, pty_needs_alloc_console:true, - has_terminal_services:true + has_terminal_services:true, + has_switch_to_thread:true }; static NO_COPY wincaps wincap_xp = { @@ -459,7 +469,8 @@ static NO_COPY wincaps wincap_xp = { supports_reading_modem_output_lines:true, needs_memory_protection:true, pty_needs_alloc_console:true, - has_terminal_services:true + has_terminal_services:true, + has_switch_to_thread:true }; static NO_COPY wincaps wincap_2003 = { @@ -500,7 +511,8 @@ static NO_COPY wincaps wincap_2003 = { supports_reading_modem_output_lines:true, needs_memory_protection:true, pty_needs_alloc_console:true, - has_terminal_services:true + has_terminal_services:true, + has_switch_to_thread:true }; wincapc wincap; diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index b1f43d6ef57..faf2469063a 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -51,6 +51,7 @@ struct wincaps unsigned needs_memory_protection : 1; unsigned pty_needs_alloc_console : 1; unsigned has_terminal_services : 1; + unsigned has_switch_to_thread : 1; }; class wincapc @@ -106,6 +107,7 @@ public: bool IMPLEMENT (needs_memory_protection) bool IMPLEMENT (pty_needs_alloc_console) bool IMPLEMENT (has_terminal_services) + bool IMPLEMENT (has_switch_to_thread) #undef IMPLEMENT }; |