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,

Speaking of Boost.Threads, this is apparently just one piece of a huge collection of libraries - the latest release takes around 11M of source files - there is no documented way to build a single library separate from others.

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: , , , ,


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:


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:


This page is powered by Blogger. Isn't yours?