Mixing C++ and Haskell, OpenSSL thread safety, and using mmap
I wrotted some messages on fa.haskell newsgroup but then I've figured that people actually read mailing lists :) So this is digest of mine recent messages on this newsgroup. First I want to say about OpenSSL thread safety. It is not thread safe by default. Who wants to import and use OpenSLL functions with FFI, have to set locking hooks for it, or else spurious crashes with useless stack trace will result. Higher level of concurrency, more likely crash will happen. Since this hooks are called by C, it is easer to setup mutexes in C module, then call Haskell main function from it. details: http://www.openssl.org/docs/crypto/threads.html Second I've successfully linked Haskell and C++ with threaded run time. Well, I'm planning to use Haskell in C++ programs and to call Haskell functions from C++ and vice versa. So far I've tried some small test programs and it seems works ok. If I launch threads from C++ then call Haskell I have to link with -threaded flag as Haskell run time complains about entering functions unsafelly without it. Also I have another question about hs_add_root. Is this neccessary? I've tried with and without call to this function and everything seems to work same way. That means I don't notice any difference. I ask this because if for example I have lot of Haskell modules do I need to call hs_add_root for every and each one? This is just a small example that works both on linux and windows, (I have different C++ versions but Haskell module is same). Haskell makes and frees array of pointers to CStrings (char**) from argument that is CString(char*). Error checking and handling is intentionally left out for now. I would appreciate any critics about it, as I've started to learn language one month ago, but have professional experience since 92 in other languages, specially C++. -- Haskell module module MakeWords where import Foreign import Foreign.C.String import Foreign.Storable import Foreign.Ptr import Foreign.Marshal.Array foreign export ccall makeWords :: CString -> IO (Ptr CString) foreign export ccall freeWords :: Ptr CString -> IO () makeWords :: CString -> IO (Ptr CString) makeWords cs = do let lst = words $ unsafePerformIO $ peekCString cs p <- mallocArray0 $ length lst makeWords' lst p return (p) makeWords' :: [String] -> Ptr CString -> IO () makeWords' [] p = do poke p nullPtr makeWords' (s:strs) p = do poke p $ unsafePerformIO $ newCString s makeWords' strs (plusPtr p $ sizeOf p) freeWords :: Ptr CString -> IO () freeWords p = do freeWords' p free p freeWords' :: Ptr CString -> IO () freeWords' p = if nullPtr /= (unsafePerformIO $ peek p) then do free $ unsafePerformIO $ peek p freeWords' $ plusPtr p $ sizeOf p else return () -- end Haskell module // C++, windows version #include <HsFFI.h> #ifdef __GLASGOW_HASKELL__ #include "makeWords_stub.h" #endif #include <iostream> #include <ostream> #include <windows.h> using namespace std; class Mutex{ public: Mutex(){ InitializeCriticalSection(&s); } ~Mutex(){ DeleteCriticalSection(&s); } void acquire(){ EnterCriticalSection(&s); } void release(){ LeaveCriticalSection(&s); } private: Mutex(const Mutex&); Mutex& operator=(const Mutex&); CRITICAL_SECTION s; }m; DWORD WINAPI tf(LPVOID arg) { for(int i=0;i<100000;++i) { const char** p = (const char**)makeWords(arg?arg:(void*)"boring default"); const char** q = p; if(!(i%10000)) { m.acquire(); while(*q){ cout<<*q++<<' '; } cout<<endl; m.release(); } freeWords(p); } return 0; } #ifdef __GLASGOW_HASKELL__ extern "C" void __stginit_MakeWords(); #endif #ifndef NUM_THREADS #define NUM_THREADS 4 #endif int main(int argc,char* argv[]) { hs_init(&argc,&argv); #if defined(__GLASGOW_HASKELL__) && defined(STGINIT_NEDEED) hs_add_root(__stginit_MakeWords); // is this neccessary? // seems works without it, // both on linux and windows #endif DWORD t[NUM_THREADS]; HANDLE h[NUM_THREADS]; char *args[NUM_THREADS] = { "abc def", "one two three", "house on the beach", "end of the line", }; for(int i =0; i<NUM_THREADS;++i) { h[i]=CreateThread(NULL,0,tf,args[i],0,&t[i]); } DWORD waitall = WaitForMultipleObjects(NUM_THREADS,h,TRUE,INFINITE); for(int i =0; i<NUM_THREADS;++i) CloseHandle(h[i]); hs_exit(); } // end C++ If someone is interested, it is possible to use g++ to link but -u flag symbols have to be obtained by -v3 flag to ghc first. Third, can I use mmap in Haskell? I see that there is MArray whitch is base for StorableArray, but I need MMappedArray which will use mmap on linux or CreateFileMapping and MapViewOfFile on windows instead of malloc. So I would appreciate if someone knows if there is such class already, but it isn;t a problem to reuse StorableArray code and create new persistent Array based on this. Greetings, Bane. _________________________________________________________________ Express yourself instantly with MSN Messenger! Download today it's FREE! http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
I use OpenSSL in a heavily threaded environment. It works without extra locking. I do not use bound (OS) threads, though. On Dec 8, 2005, at 7:06 AM, Branimir Maksimovic wrote:
First I want to say about OpenSSL thread safety. It is not thread safe by default. Who wants to import and use OpenSLL functions with FFI, have to set locking hooks for it, or else spurious crashes with useless stack trace will result. Higher level of concurrency, more likely crash will happen.
From: Joel Reymont <joelr1@gmail.com> To: "Branimir Maksimovic" <bmaxa@hotmail.com> CC: haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] Mixing C++ and Haskell, OpenSSL thread safety, and using mmap Date: Thu, 8 Dec 2005 09:21:08 +0000
I use OpenSSL in a heavily threaded environment. It works without extra locking. I do not use bound (OS) threads, though.
If code executes concurrently that means you have a problem with OpenSSL for sure. Probably it works now because SSL calls are not concurrent or so, but I wouldn't risk about it as I am sure that you would have problems with that if calls to SSL functions are concurrent. Also, I think that original problem of gethostbyname just hides real SSL problem as you've probably locked around that too, but you can't be really sure. so either lock around every SSL call with global mutex or set locks in C module then call Haskell or setup callbacks from Haskell, whichever way you prefer. Greetings, Bane.
On Dec 8, 2005, at 7:06 AM, Branimir Maksimovic wrote:
First I want to say about OpenSSL thread safety. It is not thread safe by default. Who wants to import and use OpenSLL functions with FFI, have to set locking hooks for it, or else spurious crashes with useless stack trace will result. Higher level of concurrency, more likely crash will happen.
_________________________________________________________________ Express yourself instantly with MSN Messenger! Download today it's FREE! http://messenger.msn.click-url.com/go/onm00200471ave/direct/01/
I only lock around the connectTo to avoid the gethostbyname issue. After that I set up two memory BIOs and hook them to go through a handshake. Encryption is done through the BIOs afterwards since I need to wrap the encrypted data in a header of my own (don't ask). I haven't had a problem with this so far but I might have just been lucky. I will put some locks around. On Dec 8, 2005, at 9:28 AM, Branimir Maksimovic wrote:
Also, I think that original problem of gethostbyname just hides real SSL problem as you've probably locked around that too, but you can't be really sure. so either lock around every SSL call with global mutex or set locks in C module then call Haskell or setup callbacks from Haskell, whichever way you prefer.
participants (2)
-
Branimir Maksimovic -
Joel Reymont