Thursday, December 21, 2006
Portable thread library
Given how important multithreading programming has become, it is surprisingly difficult to find simple and usable portable C/C++ thead library.
In fact, this page claims there are only two such C++ libraries in existence: zthread and Boost.Threads.
zthread offers a clean, well-thought C++ design (claimed to be modeled after Java threads). Despite the fact that Cygwin was not mentioned as one of the supported platforms, it does compile and run under Cygwin with no visible (Cygwin-specific) problems. However,
- There is a complete absence of any examples, demos, unit tests, tutorials, etc. The only documentation provided is doxygen-generated.
- It appears that the implementation, rather than establishing a set of common wrappers over win32 and POSIX APIs (and perhaps others), is trying to more or less provide its own alternative implementation, perhaps having maximum platform-independence as one of the goals. As a result, library is quite volatile and does need a very active maintenance and user base, which does not seem to be there.
- As a typical example, some users report apparent problems under VC++ 6.0 which no one is able to diagnose or to comment on (thus one needs Visual .NET to use it).
- I spent long time trying to understand why my test program (under both Cygwin and Linux) was mysteriously crashing before I realized that zthread library is probably trying to do some very "intelligent" memory management, and as a result, all zthread-objects like mutex'es or threads must be dynamically allocated (via new); but then, you never need to release them...
So, is there a better alternative?
It appears that there is: it is POSIX Threads for Win32 project. They provide ready-to use libraries that can be (dynamically) linked with any Win32 application for (almost) complete POSIX thread support. See FAQ for long and interesting discussion on various ways to handle exceptions that arise from inside the library (in my experiments, I used pthreadVC1.lib)
Finally, a good pthread tutorial and documentation is available here.
Appendix. Sample C++ application implemented with zthread:
#include <string> #include <iostream> #include <zthread/Thread.h> #include <zthread/Mutex.h> ZThread::Mutex * output = new ZThread::Mutex (); class ThreadExample : public ZThread::Runnable { public: ThreadExample(std::string thread_name, size_t iterations) : name_(thread_name), num_times_to_loop_(iterations) {} void run() { for (size_t i = 0; i < num_times_to_loop_; i++) { output->acquire(); std::cerr << i << " " << name_ << "\n"; output->release(); } output->acquire(); std::cerr << name_ << " finished! " << std::endl; output->release(); } private: std::string name_; size_t num_times_to_loop_; }; int main() { using namespace std; using namespace ZThread; try { Thread t1(new ThreadExample("Thread-1", 50)); Thread t2(new ThreadExample("Thread-2", 50)); } catch (const Synchronization_Exception& e) { cerr << e.what() << "\n"; } }
and pthread:
#include <string> #include <iostream> #include <pthread.h> pthread_mutex_t output = PTHREAD_MUTEX_INITIALIZER; class ThreadExample { public: ThreadExample(std::string thread_name, size_t iterations) : name_(thread_name), num_times_to_loop_(iterations) {} void run() { for (size_t i = 0; i < num_times_to_loop_; i++) { pthread_mutex_lock (&output); std::cerr << i << " " << name_ << std::endl; pthread_mutex_unlock (&output); } pthread_mutex_lock (&output); std::cerr << name_ << " finished! " << std::endl; pthread_mutex_unlock (&output); } static void * prun(void * self) { ((ThreadExample *)self)->run(); return NULL; } private: std::string name_; size_t num_times_to_loop_; }; int main() { pthread_t p1, p2; pthread_create(&p1, NULL, ThreadExample::prun, new ThreadExample("Thread-1", 50)); pthread_create(&p2, NULL, ThreadExample::prun, new ThreadExample("Thread-2", 50)); pthread_exit(NULL); return 0; }
Labels: C, C++, multithreading, threads, windows
Sunday, December 10, 2006
double to float conversion
When we must convert float number to integer in C, we have two convenient functions at our disposal: floor and ceil. For some reason however, there are no natural counterparts to these functions when we deal with double -> float conversion.
I wrote a simple implementation of these utilities using existing C library functions frexp and ldexp:
float fdround ( double x, int isfloor ) { const int Nf = 23; /* IEEE 754: http://en.wikipedia.org/wiki/IEEE_754 */ double m; int exp, sx; if (0 == (sx = ((x < 0.0) ? (-1) : ((x > 0.0) ? 1 : 0)))) return 0.0; m = frexp ( x * sx, &exp ); return (float)(ldexp ( (double)sx * ((isfloor ^ (sx == -1))? floor : ceil) (m * (1 << (Nf + 1))), exp - Nf - 1)); } #define fceil(x) (fdround ( (x), 0 )) #define ffloor(x) (fdround ( (x), 1 ))
This implementation of course depends on the correct knowledge of the number of bits for mantissa as per IEEE standard, which is 23 for 32-bit floating-point numbers.
Here is a simple utility to test whether the above function generates correct numbers:
void test(double x) { float f = ffloor(x), c = fceil(x); float a = 0.4 * f + 0.6 * c, b = 0.6 * f + 0.4 * c, y = (f + c)/2; assert ( f <= x && c >= x ); assert ( a == c && b == f ); assert ( y == f || y == c ); }
Enjoy!
Labels: C
Saturday, December 09, 2006
Cygwin shell hangs upon upgrade
I just updated my cygwin installation and it stopped working: on startup any cygwin shell hangs.
Quick Internet search revealed nothing, so a little research of my own was in order...
It appears that cygwin shell startup files insist that file /bin/sh be a copy of /bin/bash. This logic is coded into file /etc/profile.d/00bash.sh :
# Get here if missing, broken, ash, or old bash, so an update is needed. # Use copy, not hard or symlink, since symlinks won't work from Windows cmd # and a hardlink to a running shell can't be broken. Try in-place copy # first, but fall back to --remove-destination in case /bin/sh has different # ACLs than /bin/bash. Record the attempt in /var/log/setup.log.full. echo "`date '+%Y/%m/%d %T'` /etc/profile.d/00bash.sh:" "Attempting to update /bin/sh.exe" >> /var/log/setup.log.full 2>&1 { /bin/cp -fpuv /bin/bash.exe /bin/sh.exe || /bin/cp -puv --remove-destination /bin/bash.exe /bin/sh.exe } >> /var/log/setup.log.full 2>&1
On the other hand, cygwin upgrade only changes /bin/bash and not /bin/sh, thus triggering the above code (and more) to execute, and this was apparently causing hang (I didn't look into where exactly).
Therefore, it this happens to you, reboot your machine, and immediately afterwards, before any cygwin applications can initiate, do this (or similar depending on where your cygwin installation is)
copy /B c:\cygwin\bin\bash.exe c:\cygwin\bin\sh.exe
Labels: cygwin