GitHub plugin: fix threading issues when libcurl is build against openssl.

* Dick Hollenbeck also contributed commit r6440.  I inadvertently forgot to set
  the Bazaar author tag before I committed it.  My apologies.
* Switch to static linking of libcurl and on linux and windows and also
  statically link in only required portions of openssl.
* Add the required thread locks which openssl needs.
* Remove the get curl version call from BASEFRAME since it pulls in curl and
  openssl into every derived wxFrame class link image.
* Remove curl function from PGM_BASE, switch to atexit() instead.  Anything in
  PGM_BASE made the singletops bigger.
* Tested on Linux, Windows, and OSX.
This commit is contained in:
Dick Hollenbeck 2016-01-14 10:17:13 -05:00 committed by Wayne Stambaugh
parent 731f785256
commit f527b29e68
7 changed files with 110 additions and 128 deletions

View File

@ -12,6 +12,12 @@ include_directories(
${INC_AFTER} ${INC_AFTER}
) )
if( NOT APPLE ) # windows and linux use openssl under curl
find_package( OpenSSL REQUIRED )
endif()
# Generate header files containing shader programs # Generate header files containing shader programs
# Order of input files is significant # Order of input files is significant
add_custom_command( add_custom_command(
@ -285,7 +291,8 @@ add_dependencies( common lib-dependencies )
add_dependencies( common version_header ) add_dependencies( common version_header )
target_link_libraries( common target_link_libraries( common
${Boost_LIBRARIES} ${Boost_LIBRARIES}
# ${CURL_LIBRARIES} we dynamically link to this ON DEMAND, not at load time ${CURL_LIBRARIES}
${OPENSSL_LIBRARIES} # empty on Apple
) )

View File

@ -28,7 +28,6 @@
* @brief EDA_BASE_FRAME class implementation. * @brief EDA_BASE_FRAME class implementation.
*/ */
#include <kicad_curl/kicad_curl.h> /* Include before any wx file */
#include <wx/aboutdlg.h> #include <wx/aboutdlg.h>
#include <wx/fontdlg.h> #include <wx/fontdlg.h>
#include <wx/clipbrd.h> #include <wx/clipbrd.h>
@ -573,8 +572,6 @@ void EDA_BASE_FRAME::CopyVersionInfoToClipboard( wxCommandEvent& event )
<< ( BOOST_VERSION / 100 % 1000 ) << wxT( "." ) << ( BOOST_VERSION / 100 % 1000 ) << wxT( "." )
<< ( BOOST_VERSION % 100 ) << wxT( "\n" ); << ( BOOST_VERSION % 100 ) << wxT( "\n" );
msg_version << KICAD_CURL::GetSimpleVersion() << wxT( "\n" );
msg_version << wxT( " USE_WX_GRAPHICS_CONTEXT=" ); msg_version << wxT( " USE_WX_GRAPHICS_CONTEXT=" );
#ifdef USE_WX_GRAPHICS_CONTEXT #ifdef USE_WX_GRAPHICS_CONTEXT
msg_version << wxT( "ON\n" ); msg_version << wxT( "ON\n" );

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2015 Mark Roszko <mark.roszko@gmail.com> * Copyright (C) 2015 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors. * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -26,56 +27,106 @@
#include <wx/dynlib.h> #include <wx/dynlib.h>
#include <macros.h> #include <macros.h>
#include <fctsys.h>
#include <kicad_curl/kicad_curl.h> #include <kicad_curl/kicad_curl.h>
#include <ki_mutex.h> // MUTEX and MUTLOCK #include <ki_mutex.h> // MUTEX and MUTLOCK
#include <richio.h> #include <richio.h>
// These are even more private than class members, and since there is only // These are even more private than class members, and since there is only
// one instance of KICAD_CURL ever, these statics are hidden here to simplify the // one instance of KICAD_CURL ever, these statics are hidden here to simplify the
// client (API) header file. // client (API) header file.
static volatile bool s_initialized; static volatile bool s_initialized;
static MUTEX s_lock; static MUTEX s_lock; // for s_initialized
// Assume that on these platforms libcurl uses OpenSSL
#if defined(__linux__) || defined(_WIN32)
void (CURL_EXTERN * KICAD_CURL::easy_cleanup) ( CURL* curl ); #include <openssl/crypto.h>
CURL* (CURL_EXTERN * KICAD_CURL::easy_init) ( void );
CURLcode (CURL_EXTERN * KICAD_CURL::easy_perform) ( CURL* curl );
CURLcode (CURL_EXTERN * KICAD_CURL::easy_setopt) ( CURL* curl, CURLoption option, ... );
const char* (CURL_EXTERN * KICAD_CURL::easy_strerror) ( CURLcode );
CURLcode (CURL_EXTERN * KICAD_CURL::global_init) ( long flags );
void (CURL_EXTERN * KICAD_CURL::global_cleanup) ( void );
curl_slist* (CURL_EXTERN * KICAD_CURL::slist_append) ( curl_slist*, const char* );
void (CURL_EXTERN * KICAD_CURL::slist_free_all) ( curl_slist* );
char* (CURL_EXTERN * KICAD_CURL::version) ( void );
curl_version_info_data* (CURL_EXTERN * KICAD_CURL::version_info) (CURLversion);
static MUTEX* s_crypto_locks;
struct DYN_LOOKUP static void lock_callback( int mode, int type, const char* file, int line )
{ {
const char* name; (void)file;
void** address; (void)line;
};
// May need to modify "name" for each platform according to how libcurl is built on wxASSERT( s_crypto_locks && unsigned( type ) < unsigned( CRYPTO_num_locks() ) );
// that platform and the spelling or partial mangling of C function names. On linux
// there is no mangling. //DBG( printf( "%s: mode=0x%x type=%d file=%s line=%d\n", __func__, mode, type, file, line );)
#define DYN_PAIR( basename ) { "curl_" #basename, (void**) &KICAD_CURL::basename }
if( mode & CRYPTO_LOCK )
{
s_crypto_locks[ type ].lock();
}
else
{
s_crypto_locks[ type ].unlock();
}
}
const DYN_LOOKUP KICAD_CURL::dyn_funcs[] = { static void init_locks()
DYN_PAIR( easy_cleanup ), {
DYN_PAIR( easy_init ), s_crypto_locks = new MUTEX[ CRYPTO_num_locks() ];
DYN_PAIR( easy_perform ),
DYN_PAIR( easy_setopt ), // From http://linux.die.net/man/3/crypto_set_id_callback:
DYN_PAIR( easy_strerror ),
DYN_PAIR( global_init ), /*
DYN_PAIR( global_cleanup ),
DYN_PAIR( slist_append ), OpenSSL can safely be used in multi-threaded applications provided that at
DYN_PAIR( slist_free_all ), least two callback functions are set, locking_function and threadid_func.
DYN_PAIR( version ),
DYN_PAIR( version_info ), locking_function(int mode, int n, const char *file, int line) is needed to
}; perform locking on shared data structures. (Note that OpenSSL uses a number
of global data structures that will be implicitly shared whenever multiple
threads use OpenSSL.) Multi-threaded applications will crash at random if it
is not set.
threadid_func( CRYPTO_THREADID *id) is needed to record the
currently-executing thread's identifier into id. The implementation of this
callback should not fill in id directly, but should use
CRYPTO_THREADID_set_numeric() if thread IDs are numeric, or
CRYPTO_THREADID_set_pointer() if they are pointer-based. If the application
does not register such a callback using CRYPTO_THREADID_set_callback(), then
a default implementation is used - on Windows and BeOS this uses the
system's default thread identifying APIs, and on all other platforms it uses
the address of errno. The latter is satisfactory for thread-safety if and
only if the platform has a thread-local error number facility.
Dick: "sounds like CRYPTO_THREADID_set_callback() is not mandatory on our
2 OpenSSL platforms."
*/
CRYPTO_set_locking_callback( &lock_callback );
}
static void kill_locks()
{
CRYPTO_set_locking_callback( NULL );
delete[] s_crypto_locks;
s_crypto_locks = NULL;
}
#else
inline void init_locks() { /* dummy */ }
inline void kill_locks() { /* dummy */ }
#endif
/// At process termination, using atexit() keeps the CURL stuff out of the
/// singletops and PGM_BASE.
static void at_terminate()
{
KICAD_CURL::Cleanup();
}
void KICAD_CURL::Init() void KICAD_CURL::Init()
@ -89,56 +140,14 @@ void KICAD_CURL::Init()
if( !s_initialized ) if( !s_initialized )
{ {
// dynamically load the library. if( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK )
wxDynamicLibrary dso;
wxString canonicalName = dso.CanonicalizeName( wxT( "curl" ) );
// This is an ugly hack for MinGW builds. We should probably use something
// like objdump to get the actual library file name from the link file.
#if defined( __MINGW32__ )
canonicalName = dso.CanonicalizeName( wxT( "curl-4" ) );
canonicalName = wxT( "lib" ) + canonicalName;
#endif
if( !dso.Load( canonicalName, wxDL_NOW | wxDL_GLOBAL ) )
{
// Failure: error reporting UI was done via wxLogSysError().
std::string msg = StrPrintf( "%s not wxDynamicLibrary::Load()ed",
static_cast< const char *>( canonicalName.mb_str() ) );
THROW_IO_ERROR( msg );
}
// get addresses.
for( unsigned i=0; i < DIM(dyn_funcs); ++i )
{
*dyn_funcs[i].address = dso.GetSymbol( dyn_funcs[i].name );
if( *dyn_funcs[i].address == NULL )
{
// Failure: error reporting UI was done via wxLogSysError().
// No further reporting required here.
std::string msg = StrPrintf( "%s has no function %s",
static_cast<const char*>( canonicalName.mb_str() ),
dyn_funcs[i].name );
THROW_IO_ERROR( msg );
}
}
if( KICAD_CURL::global_init( CURL_GLOBAL_ALL ) != CURLE_OK )
{ {
THROW_IO_ERROR( "curl_global_init() failed." ); THROW_IO_ERROR( "curl_global_init() failed." );
} }
wxLogDebug( "Using %s", GetVersion() ); init_locks();
// Tell dso's wxDynamicLibrary destructor not to Unload() the program image, wxLogDebug( "Using %s", GetVersion() );
// since everything is fine before this. In those cases where THROW_IO_ERROR
// is called, dso is destroyed and the DSO/DLL is unloaded before returning in
// those error cases.
(void) dso.Detach();
s_initialized = true; s_initialized = true;
} }
@ -169,14 +178,11 @@ void KICAD_CURL::Cleanup()
if( s_initialized ) if( s_initialized )
{ {
curl_global_cleanup();
KICAD_CURL::global_cleanup(); kill_locks();
// dyn_funcs are not good for anything now, assuming process is ending soon here. atexit( &at_terminate );
for( unsigned i=0; i < DIM(dyn_funcs); ++i )
{
*dyn_funcs[i].address = 0;
}
s_initialized = false; s_initialized = false;
} }
@ -189,7 +195,7 @@ std::string KICAD_CURL::GetSimpleVersion()
if( !s_initialized ) if( !s_initialized )
Init(); Init();
curl_version_info_data *info = KICAD_CURL::version_info( CURLVERSION_NOW ); curl_version_info_data* info = curl_version_info( CURLVERSION_NOW );
std::string res; std::string res;

View File

@ -52,29 +52,24 @@ KICAD_CURL_EASY::KICAD_CURL_EASY() :
KICAD_CURL::Init(); KICAD_CURL::Init();
// Do not catch exception from KICAD_CURL::Init() at this level. m_CURL = curl_easy_init();
// Instantiation of this instance will fail if Init() throws, thus ensuring
// that this instance cannot be subsequently used.
// Caller needs a try catch around KICAD_CURL_EASY instantiation.
m_CURL = KICAD_CURL::easy_init();
if( !m_CURL ) if( !m_CURL )
{ {
THROW_IO_ERROR( "Unable to initialize CURL session" ); THROW_IO_ERROR( "Unable to initialize CURL session" );
} }
KICAD_CURL::easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, write_callback ); curl_easy_setopt( m_CURL, CURLOPT_WRITEFUNCTION, write_callback );
KICAD_CURL::easy_setopt( m_CURL, CURLOPT_WRITEDATA, (void*) &m_buffer ); curl_easy_setopt( m_CURL, CURLOPT_WRITEDATA, (void*) &m_buffer );
} }
KICAD_CURL_EASY::~KICAD_CURL_EASY() KICAD_CURL_EASY::~KICAD_CURL_EASY()
{ {
if( m_headers ) if( m_headers )
KICAD_CURL::slist_free_all( m_headers ); curl_slist_free_all( m_headers );
KICAD_CURL::easy_cleanup( m_CURL ); curl_easy_cleanup( m_CURL );
} }
@ -82,13 +77,13 @@ void KICAD_CURL_EASY::Perform()
{ {
if( m_headers ) if( m_headers )
{ {
KICAD_CURL::easy_setopt( m_CURL, CURLOPT_HTTPHEADER, m_headers ); curl_easy_setopt( m_CURL, CURLOPT_HTTPHEADER, m_headers );
} }
// bonus: retain worst case memory allocation, should re-use occur // bonus: retain worst case memory allocation, should re-use occur
m_buffer.clear(); m_buffer.clear();
CURLcode res = KICAD_CURL::easy_perform( m_CURL ); CURLcode res = curl_easy_perform( m_CURL );
if( res != CURLE_OK ) if( res != CURLE_OK )
{ {

View File

@ -30,7 +30,6 @@
* (locale handling) * (locale handling)
*/ */
#include <kicad_curl/kicad_curl.h> /* Include before any wx file */
#include <fctsys.h> #include <fctsys.h>
#include <wx/html/htmlwin.h> #include <wx/html/htmlwin.h>
#include <wx/fs_zip.h> #include <wx/fs_zip.h>
@ -289,8 +288,6 @@ void PGM_BASE::destroy()
{ {
// unlike a normal destructor, this is designed to be called more than once safely: // unlike a normal destructor, this is designed to be called more than once safely:
KICAD_CURL::Cleanup();
delete m_common_settings; delete m_common_settings;
m_common_settings = 0; m_common_settings = 0;

View File

@ -92,7 +92,7 @@ public:
*/ */
static const char* GetVersion() static const char* GetVersion()
{ {
return KICAD_CURL::version(); return curl_version();
} }
@ -103,26 +103,6 @@ public:
* @return std::string - Generated version string * @return std::string - Generated version string
*/ */
static std::string GetSimpleVersion(); static std::string GetSimpleVersion();
private:
// Alphabetically:
// dynamically looked up libcurl function pointers whose prototypes were
// taken from the system's libcurl headers.
static void (CURL_EXTERN * easy_cleanup) ( CURL* curl );
static CURL* (CURL_EXTERN * easy_init) ( void );
static CURLcode (CURL_EXTERN * easy_perform) ( CURL* curl );
static CURLcode (CURL_EXTERN * easy_setopt) ( CURL* curl, CURLoption option, ... );
static const char* (CURL_EXTERN * easy_strerror) ( CURLcode );
static CURLcode (CURL_EXTERN * global_init) ( long flags );
static void (CURL_EXTERN * global_cleanup) ( void );
static curl_slist* (CURL_EXTERN * slist_append) ( curl_slist*, const char* );
static void (CURL_EXTERN * slist_free_all) ( curl_slist* );
static char* (CURL_EXTERN * version) ( void );
static curl_version_info_data* (CURL_EXTERN * version_info) (CURLversion);
/// A tuple of ASCII function names and pointers to pointers to functions
static const DYN_LOOKUP dyn_funcs[];
}; };
#endif // KICAD_CURL_H_ #endif // KICAD_CURL_H_

View File

@ -89,7 +89,7 @@ public:
void SetHeader( const std::string& aName, const std::string& aValue ) void SetHeader( const std::string& aName, const std::string& aValue )
{ {
std::string header = aName + ':' + aValue; std::string header = aName + ':' + aValue;
m_headers = KICAD_CURL::slist_append( m_headers, header.c_str() ); m_headers = curl_slist_append( m_headers, header.c_str() );
} }
/** /**
@ -150,7 +150,7 @@ public:
*/ */
const std::string GetErrorText( CURLcode aCode ) const std::string GetErrorText( CURLcode aCode )
{ {
return KICAD_CURL::easy_strerror( aCode ); return curl_easy_strerror( aCode );
} }
/** /**
@ -163,7 +163,7 @@ public:
*/ */
template <typename T> CURLcode SetOption( CURLoption aOption, T aArg ) template <typename T> CURLcode SetOption( CURLoption aOption, T aArg )
{ {
return KICAD_CURL::easy_setopt( m_CURL, aOption, aArg ); return curl_easy_setopt( m_CURL, aOption, aArg );
} }
/** /**