[PATCH 4/9] ntdll: Add process-local handle tracking

Daniel Santos daniel.santos at pobox.com
Sun Sep 13 17:16:01 CDT 2015


Adds a facility for ntdll to track handles locally and attach
information to them, allowing for some operations to be performed
directly by the client process.
---
 dlls/ntdll/loader.c     |   1 +
 dlls/ntdll/ntdll_misc.h |  62 ++++++
 dlls/ntdll/om.c         | 568 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 631 insertions(+)

diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index 8a43310..0c779e2 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -3258,6 +3258,7 @@ void __wine_process_init(void)
     umask( FILE_umask );
 
     load_global_options();
+    ntdll_object_db_init();
 
     /* setup the load callback and create ntdll modref */
     wine_dll_set_callback( load_builtin_callback );
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index cbd19db..f0b702e 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -28,6 +28,8 @@
 #include "winnt.h"
 #include "winternl.h"
 #include "wine/server.h"
+#include "wine/rbtree.h"
+#include "wine/list.h"
 
 #define MAX_NT_PATH_LENGTH 277
 
@@ -268,4 +270,64 @@ extern HANDLE keyed_event DECLSPEC_HIDDEN;
 
 NTSTATUS WINAPI RtlHashUnicodeString(PCUNICODE_STRING,BOOLEAN,ULONG,ULONG*);
 
+enum ntdll_obj_type {
+    NTDLL_OBJ_TYPE_ASYNC,                       /* async I/O */
+    NTDLL_OBJ_TYPE_EVENT,                       /* event */
+    NTDLL_OBJ_TYPE_COMPLETION,                  /* IO completion ports */
+    NTDLL_OBJ_TYPE_FILE,                        /* file */
+    NTDLL_OBJ_TYPE_MAILSLOT,                    /* +2 chain mail slot */
+    NTDLL_OBJ_TYPE_MUTEX,                       /* mutex */
+    NTDLL_OBJ_TYPE_MSG_QUEUE,                   /* message queue */
+    NTDLL_OBJ_TYPE_PROCESS,                     /* process */
+    NTDLL_OBJ_TYPE_SEMAPHORE,                   /* semaphore */
+    NTDLL_OBJ_TYPE_THREAD,                      /* thread */
+    NTDLL_OBJ_TYPE_WAITABLE_TIMER,              /* waitable timer */
+
+    NTDLL_OBJ_TYPE_MAX
+};
+
+struct ntdll_object;
+
+struct ntdll_object_ops {
+    /* wait on the object (TODO) */
+    NTSTATUS            (*wait)        (struct ntdll_object *obj, const LARGE_INTEGER *timeout);
+    /* returns true if the object is currently signaled */
+    BOOL                (*signaled)    (struct ntdll_object *obj);
+    /* attempt to obtain lock on object, return STATUS_SUCCESS or STATUS_WAS_LOCKED upon failure. */
+    NTSTATUS            (*trywait)     (struct ntdll_object *obj);
+    /* called to release a lock previous obtained by trywait() */
+    NTSTATUS            (*trywait_undo)(struct ntdll_object *obj);
+    /* close the client-side object */
+    void                (*close)       (struct ntdll_object *obj);
+    /* Dump a description of the object to the supplied buffer. The implementation should
+     * generally call ntdll_object_dump_base() to describe it's struct ntdll_object member.*/
+    void                (*dump)        (const struct ntdll_object *obj, char **start, const char *const end);
+};
+
+/* process-local data for ntdll objects
+ *
+ * currently, fields ops - server_ptr should sit on one cache line in all cases,
+ * list_entry used infrequently */
+struct ntdll_object {
+    struct ntdll_object_ops    *ops;        /* ops */
+    int                         refcount;   /* reference count */
+    HANDLE                      h;          /* handle to the object (may be more than one handle tot he same server-side obj) */
+    enum ntdll_obj_type         type_id;    /* redundant? maybe but keep for debugging for now */
+    struct wine_rb_entry        tree_entry; /* red-black tree node */
+    ULONG_PTR                   server_ptr; /* for debugging only */
+    struct list                 list_entry;
+};
+
+extern NTSTATUS             ntdll_object_db_init(void);
+extern struct ntdll_object *ntdll_object_new(const HANDLE h, size_t size, enum ntdll_obj_type type_id, struct ntdll_object_ops *ops);
+extern struct ntdll_object *ntdll_object_grab(struct ntdll_object *obj);
+extern void                 ntdll_object_release(struct ntdll_object *obj);
+extern void                 ntdll_object_dump_base(const struct ntdll_object *obj, char **start, const char *const end);
+extern const char          *ntdll_object_dump(const struct ntdll_object *obj);
+extern int                  ntdll_handle_add(struct ntdll_object *obj);
+extern void                 ntdll_handle_remove(const HANDLE h);
+extern struct ntdll_object *ntdll_handle_find(const HANDLE h);
+
+extern void                 ntdll_server_notify_lock_release(void);
+
 #endif
diff --git a/dlls/ntdll/om.c b/dlls/ntdll/om.c
index 3fadba7..3ccce4c 100644
--- a/dlls/ntdll/om.c
+++ b/dlls/ntdll/om.c
@@ -20,6 +20,7 @@
  */
 
 #include "config.h"
+#include "wine/port.h"
 
 #include <stdarg.h>
 #include <stdlib.h>
@@ -30,6 +31,9 @@
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -40,6 +44,567 @@
 #include "wine/server.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
+WINE_DECLARE_DEBUG_CHANNEL(ntdll_obj);
+
+
+/*
+ *      Process-local object information management
+ *
+ * Theory of Operation
+ * ===================
+ * Information about some ntdll objects are cached locally and used to facilitate
+ * IPC operations natively, keeping the server informed about anything important
+ * via asynchronous messages, preventing the need for the big context switch. This
+ * works for releasing locks and attempting to wait on one or more supported objects.
+ * All other WaitFor* calls are routed to the server.
+ *
+ * Lifecycle of a struct ntdll_object object
+ *
+ * refcount     Operation
+ * (no object)  Execute a server call that creates an object & returns a handle.
+ * 1            Call ntdll_object_new() passing handle to allocate a new struct ntdll_object.
+ *              * allocates & zeros struct ntdll_object
+ *              * adds to objects.list (double-linked list)
+ * 1            Initialize derived-type data members
+ * 2            Call ntdll_handle_add() to index handle in handles.tree (red-black tree)
+ * 1            Call ntdll_object_release() when done with the object.
+ * ....
+ * (no object)  We receive an API call that we might be able to manage client-side
+ * 2            Call ntdll_handle_find() on the handle and get the object
+ * 2            Perform whatever we need to do locally
+ * 1            Call ntdll_object_release() when done with the object.
+ * ...
+ * (no object)  We receive a call to NtClose (CloseHandle)
+ * 2            Call ntdll_handle_find() on the handle and get the object
+ * 1            Call ntdll_handle_remove() to remove it from handles.tree.
+ * 0            Call ntdll_object_release()
+ *              * object is removed from list
+ *              * refcount reaches zero and destructor ops->close() is called
+ *              * memory is freed
+ *
+ * Because we're doing this in the multi-threaded environment of the client process instead of
+ * the safety of the single-threaded server, things won't always go as above. It is possible
+ * for a call to NtClose to be received while another thread is still using the object, so
+ * that the object returned by Call ntdll_handle_find() may have a refcount of 3 or more.
+ * However, the object should be immediately delisted, so we can have many live objects
+ * with the same handle, but only one of those handles (the one in the tree) is the living
+ * one.
+ *
+ * TODO: Make sure that UB is acceptable for when a program tries to use a stale handle, or
+ * should we zero the handle when it's removed from the tree (NtClose returns).
+ */
+
+
+/* FIXME: portability */
+static __thread char *tls_debug_buffer = NULL;
+#define NTDLL_DEBUG_BUFFER_SIZE 0x400
+
+static const char * const obj_type_desc[NTDLL_OBJ_TYPE_MAX] = {
+    "async I/O",                                /* NTDLL_OBJ_TYPE_ASYNC */
+    "event",                                    /* NTDLL_OBJ_TYPE_EVENT */
+    "I/O completion ports",                     /* NTDLL_OBJ_TYPE_COMPLETION */
+    "file",                                     /* NTDLL_OBJ_TYPE_FILE */
+    "mail slot",                                /* NTDLL_OBJ_TYPE_MAILSLOT */
+    "mutex",                                    /* NTDLL_OBJ_TYPE_MUTEX */
+    "message queue",                            /* NTDLL_OBJ_TYPE_MSG_QUEUE */
+    "process",                                  /* NTDLL_OBJ_TYPE_PROCESS */
+    "semaphore",                                /* NTDLL_OBJ_TYPE_SEMAPHORE */
+    "thread",                                   /* NTDLL_OBJ_TYPE_THREAD */
+    "waitable timer",                           /* NTDLL_OBJ_TYPE_WAITABLE_TIMER */
+};
+
+/* List containing all process-locally known objects */
+static struct {
+    struct list         list;   /* list of all struct ntdll_object ojbects */
+    pthread_mutex_t     mutex;  /* lock for modifying list */
+} objects = {
+    {NULL, NULL},
+    PTHREAD_MUTEX_INITIALIZER,  /* non-recursive fast lock */
+};
+
+/*************************************************************************
+ * ntdll_object_new
+ *
+ * Allocates and initializes a new struct ntdll_object object.
+ *
+ * PARAMS
+ *  h          [I] Handle of the object.
+ *  size       [I] Number of bytes to allocate.
+ *  type_id    [I] Integral type of the object
+ *  ops        [I] Pointer to function table.
+ *
+ * RETURNS
+ *  Success: A pointer to the new object.
+ *  Failure: Null, indicating a low memory condition.
+ *
+ * NOTES
+ *  Allocates and initialises a struct ntdll_object object and adds it to
+ *  process-global list. Initial refcount is 1. After the derived-type
+ *  initialises its members the object should be added to the handle database
+ *  by calling ntdll_handle_add(), which will increase its refcount to 2. Once
+ *  this is done, the object should be released with ntdll_object_release().
+ *  If you release it before that, the object will auto-destruct.
+ *
+ * LOCKING
+ *  objects.mutex
+ */
+struct ntdll_object *ntdll_object_new(const HANDLE h, size_t size, enum ntdll_obj_type type_id, struct ntdll_object_ops *ops)
+{
+    struct ntdll_object *obj;
+
+    assert(size >= sizeof(*obj));
+    assert(type_id < NTDLL_OBJ_TYPE_MAX);
+
+    TRACE_(ntdll_obj)("%p, %zu, %u (%s), %p\n", h, size, type_id, obj_type_desc[type_id], ops);
+
+    obj = RtlAllocateHeap(GetProcessHeap(), 0, size);
+    if (obj)
+    {
+        memset(obj, 0, size);
+        obj->h          = h;
+        obj->refcount   = 1;
+        obj->type_id    = type_id;
+        obj->ops        = ops;
+        obj->server_ptr = 0;
+        pthread_mutex_lock(&objects.mutex);
+        list_add_tail(&objects.list, &obj->list_entry);
+        pthread_mutex_unlock(&objects.mutex);
+    } else
+        ERR("Failed to alloc %zu bytes\n", sizeof(*obj));
+
+    return obj;
+}
+
+/*************************************************************************
+ * ntdll_object_grab
+ *
+ * Increases refcount by one.
+ *
+ * PARAMS
+ *  obj        [I] The object.
+ */
+struct ntdll_object *ntdll_object_grab(struct ntdll_object *obj)
+{
+    int refcount = interlocked_xchg_add(&obj->refcount, 1);
+    assert(refcount < INT_MAX && refcount > 0);
+    return obj;
+}
+
+/*************************************************************************
+ * ntdll_object_release
+ *
+ * Decrease refcount by one, possibly destroying the object
+ *
+ * PARAMS
+ *  obj        [I] The object.
+ *
+ * NOTES
+ *  While using an object, it is possible for another thread to call
+ *  CloseHandle(), removing it from the handle database (and decreasing its
+ *  refcount). Therefore you should not use the pointer after a call to
+ *  ntdll_object_release() returns.
+ *
+ * LOCKING
+ *  objects.mutex
+ */
+void ntdll_object_release(struct ntdll_object *obj)
+{
+    int refcount = interlocked_xchg_add(&obj->refcount, -1);
+    assert(refcount > 0);
+
+    if (!refcount)
+    {
+        /* TODO: check for any more race conditions */
+        TRACE_(ntdll_obj)("Removing & freeing object %s\n", ntdll_object_dump(obj));
+        pthread_mutex_lock(&objects.mutex);
+        list_remove(&obj->list_entry);
+        pthread_mutex_unlock(&objects.mutex);
+        if (obj->ops->close)
+            obj->ops->close(obj);
+#if defined(DEBUG) || defined(DEBUG_OBJECTS)
+        memset(obj, 0xaa, sizeof(struct ntdll_object));
+#endif
+        RtlFreeHeap(GetProcessHeap(), 0, obj);
+    }
+}
+
+/*************************************************************************
+ * ntdll_object_dump_base
+ *
+ * Called by derived-class to dump a text description of base class members.
+ *
+ * PARAMS
+ *  obj         [I]  The object to dump
+ *  start       [IO] Pointer to a pointer to the next write position.
+ *  end         [I]  Pointer to one byte past the end of the buffer.
+ *
+ * NOTES
+ *  Called by derived class to dump struct ntdll_object to a string buffer.
+ *  The pointer that start points to will be updated to the new end of the
+ *  text so that subsequent writes may begin there.
+ */
+void ntdll_object_dump_base(const struct ntdll_object *obj, char **start, const char *const end)
+{
+    int count;
+
+    assert(start && *start && end);
+    assert(end > *start);
+
+    if (!obj)
+        count = snprintf(*start, end - *start, "(NULL)");
+    else
+    {
+        assert(obj->type_id < NTDLL_OBJ_TYPE_MAX);
+
+        count = snprintf(*start, end - *start,
+                         "%p {"
+                         "ops = %p, "
+                         "refcount = %u, "
+                         "h = %p, "
+                         "type_id = %u (%s), "
+                         "tree_entry = {left = %p, right = %p, flags = 0x%x}, "
+                         "server_ptr = %lx, "
+                         "list_entry = {next = %p, prev = %p}"
+                         "}",
+                         obj,
+                         obj->ops,
+                         obj->refcount,
+                         obj->h,
+                         obj->type_id, obj_type_desc[obj->type_id],
+                         obj->tree_entry.left, obj->tree_entry.right, obj->tree_entry.flags,
+                         obj->server_ptr,
+                         obj->list_entry.next, obj->list_entry.prev);
+    }
+
+    if (count < 0)
+    {
+        perror("snprintf");
+        return;
+    }
+
+    *start += count;
+}
+
+/* return a thread-local buffer for dumping object descriptions to */
+static void *ntdll_object_get_debug_buffer(size_t *size)
+{
+    /* FIXME: free this on thread exit (not sure where that is hooked) */
+    if (!tls_debug_buffer)
+        tls_debug_buffer = RtlAllocateHeap(GetProcessHeap(), 0, NTDLL_DEBUG_BUFFER_SIZE);
+
+    if (size)
+        *size = tls_debug_buffer ? NTDLL_DEBUG_BUFFER_SIZE : 0;
+    return tls_debug_buffer;
+}
+
+/*************************************************************************
+ * ntdll_object_dump
+ *
+ * Dumps a text description of the object to a thread-local buffer.
+ *
+ * PARAMS
+ *  obj         [I]  The object to dump
+ *
+ * RETURNS
+ *  A pointer to a text description of the object.
+ *
+ * NOTES
+ *  Because this function uses a thread-local buffer, you must use the result
+ *  before calling it again. For instance, this code will not work:
+ *
+ *  ERR("%s will look the same as %s\n", ntdll_object_dump(o1), ntdll_object_dump(02));
+ */
+const char *ntdll_object_dump(const struct ntdll_object *obj)
+{
+    char *start;
+    const char *ret;
+    size_t size;
+
+    if (!(start = ntdll_object_get_debug_buffer(&size)))
+        return NULL;
+
+    ret = start;
+    if (!obj)
+        snprintf(start, size, "(NULL)");
+    else if (obj->ops->dump)
+        obj->ops->dump(obj, &start, start + size);
+    else
+        ntdll_object_dump_base(obj, &start, start + size);
+
+    return ret;
+}
+
+/* red-black tree functions for handle table */
+static inline void *ntdll_object_rb_alloc(size_t size)
+{
+    return RtlAllocateHeap(GetProcessHeap(), 0, size);
+}
+
+static inline void *ntdll_object_rb_realloc(void *ptr, size_t size)
+{
+    return RtlReAllocateHeap(GetProcessHeap(), 0, ptr, size);
+}
+
+static inline void ntdll_object_rb_free(void *ptr)
+{
+    RtlFreeHeap(GetProcessHeap(), 0, ptr);
+}
+
+static inline int ntdll_object_handle_compare(const void *key, const struct wine_rb_entry *entry)
+{
+    const HANDLE *_a = key;
+    const HANDLE *_b = &WINE_RB_ENTRY_VALUE(entry, const struct ntdll_object, tree_entry)->h;
+
+    return *_a > *_b ? 1 : (*_a < *_b ? -1 : 0);
+}
+
+static const struct wine_rb_functions obj_handles_rb_ops =
+{
+    ntdll_object_rb_alloc,
+    ntdll_object_rb_realloc,
+    ntdll_object_rb_free,
+    ntdll_object_handle_compare,
+};
+
+/* Tree mapping all process-locally known kernel handles to a struct ntdll_object */
+static struct {
+    struct wine_rb_tree tree;
+    pthread_mutex_t mutex;
+} handles = {
+    { &obj_handles_rb_ops, NULL, {NULL, 0, 0}}, /* static initializer to aid -findirect-inline */
+    PTHREAD_MUTEX_INITIALIZER                   /* non-recursive fast lock */
+};
+
+/*************************************************************************
+ * ntdll_handle_add
+ *
+ * Adds the object to the ntdll handle database
+ *
+ * PARAMS
+ *  obj        [I] The object.
+ *
+ * NOTES
+ *  Adds the object to the handle database, increasing its reference count to
+ *  account for it being referenced by the handle tree.
+ *
+ * LOCKING
+ *  handles.mutex
+ */
+__attribute__((noinline, flatten))
+int ntdll_handle_add(struct ntdll_object *obj)
+{
+    int ret = 0;
+
+    TRACE_(ntdll_obj)("%p\n", obj);
+
+try_again:
+    pthread_mutex_lock(&handles.mutex);
+    ret = wine_rb_put(&handles.tree, &obj->h, &obj->tree_entry);
+
+    /* increase the ref count */
+    if (!ret)
+        ntdll_object_grab(obj);
+    pthread_mutex_unlock(&handles.mutex);
+
+    if (ret)
+    {
+        struct ntdll_object *existing = ntdll_handle_find(obj->h);
+
+        /* bug if we get here -- this should be resolved now, but lets leave
+         * this here for now to be safe */
+        ERR("Failed to insert handle %p.\n", obj->h);
+        if (existing)
+        {
+            ERR("Existing object: %s\n", ntdll_object_dump(existing));
+            ntdll_object_release(existing);
+        }
+        else
+            ERR("No other object found with that handle...\n");
+        ERR("New object: %s\n", ntdll_object_dump(obj));
+
+        assert(0);
+
+        /* should probably just exit(1) here */
+        ntdll_handle_remove(obj->h);
+        goto try_again;
+    }
+
+    TRACE("Added obj %s\n", ntdll_object_dump(obj));
+
+    return ret;
+}
+
+/*************************************************************************
+ * ntdll_handle_remove
+ *
+ *  Remove an object from the handle database.
+ *
+ * PARAMS
+ *  h           [I] A handle to remove.
+ *
+ * NOTES
+ *  If no object with the specified handle is found then nothing is done.
+ *
+ * LOCKING
+ *  handles.mutex
+ */
+__attribute__((noinline))
+void ntdll_handle_remove(const HANDLE h)
+{
+    struct ntdll_object *obj = ntdll_handle_find(h);
+
+    if (!obj)
+        return;
+
+    TRACE_(ntdll_obj)("h = %p) obj = %s\n", h, ntdll_object_dump(obj));
+
+    pthread_mutex_lock(&handles.mutex);
+    /* FIXME: a more efficient wine_rb_remove_by_entry would be nice here */
+    wine_rb_remove(&handles.tree, &h);
+    pthread_mutex_unlock(&handles.mutex);
+
+    ntdll_object_release(obj); /* once for removing it from the tree */
+    ntdll_object_release(obj); /* once for the call to ntdll_handle_find() */
+}
+
+/*************************************************************************
+ * ntdll_handle_find
+ *
+ *  Searches the process-local handle database for the specified object.
+ *
+ * PARAMS
+ *  h           [I] Handle of an object.
+ *
+ * NOTES
+ *  If an object with a matching handle is found then the object's refcount is
+ *  incremented and a pointer to the object is returned. It is then the
+ *  responsibility of the caller to release the object when done.
+ *
+ * RETURNS
+ *  Success: A pointer to the object.
+ *  Failure: NULL
+ *
+ * LOCKING
+ *  handles.mutex
+ */
+struct ntdll_object *ntdll_handle_find(const HANDLE h)
+{
+    struct wine_rb_entry *entry;
+    struct ntdll_object *ret = NULL;
+
+    pthread_mutex_lock(&handles.mutex);
+    entry = wine_rb_get(&handles.tree, &h);
+
+    if (entry)
+    {
+        ret = WINE_RB_ENTRY_VALUE(entry, struct ntdll_object, tree_entry);
+        ntdll_object_grab(ret);
+    }
+    pthread_mutex_unlock(&handles.mutex);
+
+    //TRACE_(ntdll_obj)("(%p) result: %p\n", h, ret);
+
+    return ret;
+}
+
+
+/* callback for ntdll_objects_cleanup()
+ *
+ * LOCKING
+ *  Locks objects.mutex (via ntdll_object_release())
+ *  Called when handles.mutex already locked
+ */
+
+static void ntdll_objects_cleanup_cb(struct wine_rb_entry *entry, void *context)
+{
+    struct ntdll_object *obj = WINE_RB_ENTRY_VALUE(entry, struct ntdll_object, tree_entry);
+    size_t *leaked_handles = (size_t *)context;
+
+    ++(*leaked_handles);
+
+    ERR_(ntdll_obj)("Leaked object handle: %s\n", ntdll_object_dump(obj));
+
+    /* handles.mutex already locked */
+    wine_rb_remove(&handles.tree, &obj->h);
+
+    ntdll_object_release(obj);
+}
+
+/* atexit() cleanup
+ *
+ * Locking:
+ *      Locks handles.mutex --> then objects.mutex
+ */
+static void ntdll_objects_cleanup(void)
+{
+    size_t leaked_handles = 0;
+    size_t leaked_objects = 0;
+
+    TRACE_(ntdll_obj)("\n");
+    pthread_mutex_lock(&handles.mutex);
+    wine_rb_for_each_entry(&handles.tree, ntdll_objects_cleanup_cb, &leaked_handles);
+    pthread_mutex_unlock(&handles.mutex);
+
+    pthread_mutex_lock(&objects.mutex);
+    leaked_objects = list_count(&objects.list);
+    if (leaked_objects)
+    {
+        struct ntdll_object *obj;
+        //struct list *i;
+        LIST_FOR_EACH_ENTRY( obj, &objects.list, struct ntdll_object , list_entry )
+        {
+            ERR_(ntdll_obj)("Leaked object: %s\n", ntdll_object_dump(obj));
+        }
+    }
+    pthread_mutex_unlock(&objects.mutex);
+
+    if (leaked_handles || leaked_objects)
+        ERR_(ntdll_obj)("*** %zu leaked handles found, %zu leaked objects remain.\n",
+                        leaked_handles, leaked_objects);
+
+}
+
+/*************************************************************************
+ * ntdll_object_db_init
+ *
+ *  Initialize objects list and handles tree
+ *
+ * NOTES
+ *  Called via __wine_process_init() in loader.c
+ *
+ * LOCKING
+ *  First locks objects.mutex, then releases and locks handles.mutex
+ */
+NTSTATUS ntdll_object_db_init(void)
+{
+    NTSTATUS ret = 0;
+
+    TRACE_(ntdll_obj)("\n");
+
+    /* init objects list if not already inited */
+    pthread_mutex_lock(&objects.mutex);
+    if (!objects.list.next)
+        list_init(&objects.list);
+    pthread_mutex_unlock(&objects.mutex);
+
+    /* init red-black handle-to-object tree if not already inited */
+    pthread_mutex_lock(&handles.mutex);
+    if (!handles.tree.stack.entries)
+    {
+        /* passing handles.tree.functions instead of obj_handles_rb_ops to aid -findirect-inline
+         * (it might not matter) */
+        if (wine_rb_init(&handles.tree, handles.tree.functions) == -1)
+        {
+            ERR("Failed to initialize ntdll object handle rbtree.\n");
+            ret = ERROR_OUTOFMEMORY;
+        }
+        else
+            atexit(ntdll_objects_cleanup);
+    }
+    pthread_mutex_unlock(&handles.mutex);
+
+    return ret;
+}
 
 
 /*
@@ -390,6 +955,9 @@ NTSTATUS close_handle( HANDLE handle )
     }
     SERVER_END_REQ;
     if (fd != -1) close( fd );
+
+    ntdll_handle_remove( handle );
+
     return ret;
 }
 
-- 
2.4.6




More information about the wine-devel mailing list