diff options
author | Daniel Drake <dan@reactivated.net> | 2010-07-29 12:18:45 +0100 |
---|---|---|
committer | Daniel Drake <dan@reactivated.net> | 2010-07-29 12:18:45 +0100 |
commit | 1867c7103b51e21432a11df8893050a4fe60ba42 (patch) | |
tree | ec331b4c8cea2c1e866e7d93d56d17d917c524c5 | |
parent | 30b5fbb87c76dfc00041404d11003e2ed068384b (diff) | |
download | libusb-1867c7103b51e21432a11df8893050a4fe60ba42.tar.gz |
Add reference counting to default contextpbr285
Michael Plante pointed out that if 2 users call libusb_init(NULL) within
a process, we end up creating 2 default contexts, one of which is lost.
Add reference counting so that the default context is reused and
destroyed only after the last user.
-rw-r--r-- | libusb/core.c | 48 |
1 files changed, 38 insertions, 10 deletions
diff --git a/libusb/core.c b/libusb/core.c index c39f33a..5c29eac 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -41,6 +41,7 @@ const struct usbi_os_backend * const usbi_backend = &windows_backend; #endif struct libusb_context *usbi_default_context = NULL; +static int default_context_refcnt = 0; static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; /** @@ -310,6 +311,14 @@ if (cfg != desired) * is created the first time a process calls libusb_init() when no other * context is alive. Contexts are destroyed during libusb_exit(). * + * The default context is reference-counted and can be shared. That means that + * if libusb_init(NULL) is called twice within the same process, the two + * users end up sharing the same context. The deinitialization and freeing of + * the default context will only happen when the last user calls libusb_exit() + * In other words, the default context is created and initialized when its + * reference count goes from 0 to 1, and is deinitialized and destroyed when + * its reference count goes from 1 to 0. + * * You may be wondering why only a subset of libusb functions require a * context pointer in their function definition. Internally, libusb stores * context pointers in other objects (e.g. libusb_device instances) and hence @@ -1476,9 +1485,15 @@ API_EXPORTED void LIBUSB_API libusb_set_debug(libusb_context *ctx, int level) /** \ingroup lib * Initialize libusb. This function must be called before calling any other * libusb function. + * + * If you do not provide an output location for a context pointer, a default + * context will be created. If there was already a default context, it will + * be reused (and nothing will be initialized/reinitialized). + * * \param context Optional output location for context pointer. * Only valid on return code 0. * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \see contexts */ API_EXPORTED int LIBUSB_API libusb_init(libusb_context **context) { @@ -1488,13 +1503,17 @@ API_EXPORTED int LIBUSB_API libusb_init(libusb_context **context) usbi_mutex_static_lock(&default_context_lock); if (!context && usbi_default_context) { + r = 0; + usbi_dbg("reusing default context"); + default_context_refcnt++; usbi_mutex_static_unlock(&default_context_lock); - return 0; /* using default; nothing to do. */ + return 0; } + ctx = malloc(sizeof(*ctx)); if (!ctx) { - usbi_mutex_static_unlock(&default_context_lock); - return LIBUSB_ERROR_NO_MEM; + r = LIBUSB_ERROR_NO_MEM; + goto err_unlock; } memset(ctx, 0, sizeof(*ctx)); #ifdef USBI_TIMERFD_AVAILABLE @@ -1552,6 +1571,8 @@ err_destroy_mutex: usbi_mutex_destroy(&ctx->usb_devs_lock); err_free_ctx: free(ctx); +err_unlock: + usbi_mutex_static_unlock(&default_context_lock); return r; } @@ -1565,6 +1586,20 @@ API_EXPORTED void LIBUSB_API libusb_exit(struct libusb_context *ctx) USBI_GET_CONTEXT(ctx); usbi_dbg(""); + /* if working with default context, only actually do the deinitialization + * if we're the last user */ + if (ctx == usbi_default_context) { + usbi_mutex_static_lock(&default_context_lock); + if (--default_context_refcnt > 0) { + usbi_dbg("not destroying default context"); + usbi_mutex_static_unlock(&default_context_lock); + return; + } + usbi_dbg("destroying default context"); + usbi_default_context = NULL; + usbi_mutex_static_unlock(&default_context_lock); + } + /* a little sanity check. doesn't bother with open_devs locking because * unless there is an application bug, nobody will be accessing this. */ if (!list_empty(&ctx->open_devs)) @@ -1574,13 +1609,6 @@ API_EXPORTED void LIBUSB_API libusb_exit(struct libusb_context *ctx) if (usbi_backend->exit) usbi_backend->exit(); - usbi_mutex_static_lock(&default_context_lock); - if (ctx == usbi_default_context) { - usbi_dbg("freeing default context"); - usbi_default_context = NULL; - } - usbi_mutex_static_unlock(&default_context_lock); - usbi_mutex_destroy(&ctx->open_devs_lock); usbi_mutex_destroy(&ctx->usb_devs_lock); free(ctx); |