curl-library
living without global variables
Date: Sun, 27 Nov 2005 22:45:39 +0100
I have written the code we discussed a while back to make it easier to
write a program in which libcurl doesn't mess with global state.
A reminder of the strategy: A libcurl user uses one of two paradigms:
- traditional and easy: Call curl_global_init() before using libcurl
and curl_global_cleanup() afterward. Make sure nothing else in your
program is using libcurl or OpenSSL, Winsock, etc. Create session
objects with curl_easy_init().
- private client object: Call (new) curl_client_init() to create a client
object. Create session objects with (new) curl_easy_init_c() with
the client handle as argument. By default, nothing that requires
libcurl to mess with global state works. If user wants it to work,
he initializes the particular global facility in his own code and
calls e.g. curl_use_global_openssl() to give libcurl permission to
use it with a particular client object.
There is still the middle ground where you call curl_global_init()
with flags that cause it to omit certain initialization, but I see
that as useful only for backward compatibility. A user would be
better off using a private client object if he wants to manage global
state explicitly.
The goal of the private client object paradigm is twofold:
- Put global state manipulation in global code, where it is less
covert and easier to manage for the use of multiple parties
(e.g. where two modules that use libcurl and one that uses OpenSSL
independently are all linked into the same program).
- Allow for the global state to be removed completely in the future,
assuming OpenSSL, etc. get fixed to be modular. (And I intend to
work on at least OpenSSL in this respect). The libcurl private
client object is where the openssl private client handle will be
kept.
I gave up on making the memory allocator stuff non-global. It is too
much work and I think too rare that someone actually sets one of those
global variables. So all I did for that is create a new function
curl_global_set_mem() to set those for a program that avoids
curl_global_init().
The implementation is pretty simple:
- A client handle (type CURLC) is analogous to an easy or multi
handle. There is a new client.c, client.h, and libcurl-client.3
to implement it
- A field added to struct SessionHandle: handle of client to which
session belongs.
- curl_global_init() creates a client handle and puts its handle in
a global variable. This is the "global client."
- curl_easy_init() makes the session belong to the global client.
- (new) curl_easy_init_c() is same as curl_easy_init() but sets the
session handle to the private client handle indicated by its
argument instead of to the global client.
- The client descriptor has a flag for each of the 5 global
facilities: Windows socket library, Amiga socket library, OpenSSL,
GNU TLS, and CHARSET environment variable. There is a new external
function to set each, and an internal one to query it.
- At various places (6 of them) where one of the global facilities
is necessary, code check whether it's OK to use it and fails
whatever it's doing if not. In the SSL cases, it does the same as
it would if there were no SSL library bound: continues the
operation, but without SSL enabled.
I have attached the 3 new files and a patch for the rest. I also
included a diff file that is the same as the patch, but with mere
indentation changes (because something moved inside an "if") ignored
so you can see better what changed.
The patch includes the man file changes that explain in greater detail
than this email the whole global state issue.
-- Bryan Henderson Phone 408-621-2000 San Jose, California
- application/octet-stream attachment: curlclient.diff
- text/x-csrc attachment: client.c
- application/octet-stream attachment: client.h
- application/octet-stream attachment: libcurl-client.3
- application/octet-stream attachment: curlclient.patch