 | 2010-09-07 ed is not dead |
 | 2010-08-26 Installing Perl modules in a non root environment |
 | 2010-08-22 Magic self leviation |
 | 2010-08-20 Google Chrome does not support offline Gmail |
 | 2010-08-19 The number 48 |
 | 2010-08-12 Welsh trout mini HOWTO |
 | 2010-08-04 Fooling a NetCache proxy into fetching forbidden files |
 | 2010-07-30 The world will end on May 21, 2011 |
 | 2010-07-28 Hiding or showing a textbox with image animation using JQuery |
 | 2010-07-27 Manipulating browser cookies using Javascript |
 | 2010-07-25 Survival of the fittest book |
 | 2010-07-23 Pastafarians in Spain |
 | 2010-07-22 You have two sheep |
 | 2010-07-09 Highway bank fire |
 | 2010-07-08 Setting up a remote git repository |
 | 2010-07-06 Bye bye trusted old Macbook |
 | 2010-06-28 John Cleese on Football |
 | 2010-06-23 ABN Amro and the Pathetic Customer Service Dept. |
 | 2010-06-22 Wally does not like criticism |
 | 2010-06-14 Soccermatch Netherlands vs Denmark |
 | 2010-06-13 Lazy Cat |
 | 2010-06-08 Reading public Buzz using the Google API |
 | 2010-06-07 A Personal Letter from Steve Martin |
 | 2010-06-05 Sushi Saturday |
 | 2010-06-04 Suppressing the Enter key with Javascript |
 | 2010-05-31 Temporal spacial anomaly on the Dutch highway |
 | 2010-05-23 Greenhost will not log your traffic |
 | 2010-05-10 Jarlsberg Webapp Exploits |
 | 2010-05-04 A Thought Experiment |
 | 2010-05-03 SafeEdit information updated |
 | 2010-05-01 Microproxy now supports ftp |
 | 2010-04-30 What could get Data angry |
 | 2010-04-29 Lego Mindstorm solving the Rubik Cube |
 | 2010-04-28 Crossroads 2.65 is out |
 | 2010-04-17 Goggomobil in its natural habitat |
 | 2010-04-14 Bacon Time |
 | 2010-04-11 104 More friends to connect with |
 | 2010-04-10 Bacteria infested radio reporter |
 | 2010-04-07 The Kubat STAR |
 | 2010-03-30 Homework Essay |
 | 2010-03-29 C++ mutexes again |
|
I wrote before about C++,
threads, and mutexes. Well, as things go, ideas evolve and what I'd
thought of as 'done and over with', came back at me.
The actual reason is that last thursday I was invited at the
State Univ. of Groningen to present a C++ lecture, and I'd picked
daemons, forking, threading, mutexes, and the whole shebang. I
presented the following snippet of how globals could be locked in a
threaded program:
// EXAMPLE 1
// Write something to cout, without other threads interfering in the displayed line.
mutex_lock(&cout);
cout << "Hello World!\n";
mutex_unlock(&cout);
// EXAMPLE 2
// Function gethostbyname() (DNS resolving) is known to be not thread-safe in many implementations
mutex_lock(gethostbyname);
struct hostent *hostaddr = gethostbyname("www.kubat.nl");
if (hostaddr) {
.
. Host successfully resolved
.
} else {
.
. Hostname could not be resolved
.
}
mutex_unlock(gethostbyname);
The idea is that mutex_lock() can set a lock on any object, via
its address (a void*). The reverse, mutex_unlock(), releases
the lock.
How not to do it
The work is of course done in the mentioned
functions mutex_lock() and mutex_unlock(). A
controversial implementation is shown below.. read on why it's
controversial, or can you spot it right away?
typedef std::map<void *, pthread_mutex_t> LockMap;
static LockMap lockmap;
static void lock_ptr(void *p) {
if (lockmap.find(p) == lockmap.end() && pthread_mutex_init(&lockmap[p], 0))
throw "Failed to initialize mutex";
if (pthread_mutex_lock(&lockmap[p]))
throw "Failed to lock mutex";
}
static void unlock_ptr(void *p) {
if (pthread_mutex_unlock(&lockmap[p]))
throw "Failed to unlock mutex";
}
void mutex_lock(void *p) {
lock_ptr(&lockmap);
lock_ptr(p);
unlock_ptr(&lockmap);
}
void mutex_unlock(void *p) {
unlock_ptr(p);
}
Internally, the functions use a map to find the right
mutexes for each void* that's being handled. When locking
something, function mutex_lock() first locks the entire map of
locks, and then calls lock_ptr(p) to set a lock for that
specific object. However, when unlocking, mutex_unlock()
does not lock the entire map - the idea being that the map
isn't being expanded, just read.
What's the problem?
"Aha," said the audience (specifically Thomas ten C., thanks for
all the comments!), "there's a potential problem there!" The
implementation of the Standard Template Library (STL) isn't guaranteed
to be thread-safe; e.g., consider the following:
- A thread is unlocking an object. It is at the
statement lockmap[p] in line 12.
- Another thread is at that time locking, and is at line 5,
at lockmap.find(p) == lockmap.end().
- But, if the STL implementation for std::map isn't
thread-safe, then these two threads might mess up the underlying map
beyond imagination...
I had assumed that reading the map wouldn't modify its
layout, so that entire map locking would be only necesary during
the writing phase, i.e., the locking of a particular object.
Not necessarily true. "Assume nothing", I should've known... Thomas
commented in a mail:
 | Microsoft writes: "A single object is thread safe
for reading from multiple threads. [...] If a single object is being
written to by one thread, then all reads and writes to that object on
the same or other threads must be protected." |
 | The SGI
implementation, and the GNU implementation which uses this, is
thread-safe when reading: "The
SGI implementation of STL is thread-safe only in
the sense that simultaneous accesses to distinct containers are safe,
and simultaneous read accesses to to shared containers are safe."
Gnu uses the same definition: "We currently use
the SGI STL definition of thread safety." |
Now what
Right. So where does that leave us? There could be several approaches
to avoid errors that involve a combination of STL containers and mutex
locking. One is, to apply a much wider lock scope before
handling std::map. Or, to also lock the entire map during the
unlocking phase. Hmmm... Another one is, not to use std::map,
but to control the implementation where std::map isn't
clearly defined. That means implementing the equivalent of a map in
code which is guaranteed not to reshuffle anything in read/only
mode. And that code is exactly what's shown below, up to the global
functions mutex_lock() and mutex_unlock(). The below
sections show and explain what's going on.
If you are interested in a source file having all this code
inside: you can grab it here. All classes
and members are contained in one file - including a main()
function to test it all. If you want to use it, then feel free to
split it up and customize it however you like.
Basic Mutex
A basic mutex is implemented in the class Mutex (how fitting).
All it does, is initialize a mutex if necessary, lock it, or unlock
it. For reasons of brevity, strings are thrown when errors occur.
class Mutex {
public:
Mutex(): _initialized(false) { }
void lock();
void unlock();
private:
pthread_mutex_t _mutex;
bool _initialized;
};
void Mutex::lock() {
if (!_initialized) {
_initialized = true;
if (pthread_mutex_init(&_mutex, 0))
throw "Failed to initialize mutex";
}
if (pthread_mutex_lock(&_mutex))
throw "Failed to lock mutex";
}
void Mutex::unlock() {
if (pthread_mutex_unlock(&_mutex))
throw "Failed to unlock mutex";
}
Mutex Nodes
Internally, the mutexes will be stored as a tree - having leaves. The next
level is therefore a node, which has a mutex, a left branch, and a right branch:
class MutexNode {
public:
MutexNode(void *o);
~MutexNode();
void obj(void *o) { _obj = o; }
void *obj() const { return _obj; }
void left(MutexNode *l) { _left = l; }
MutexNode *left() const { return _left; }
void right(MutexNode *l) { _right = l; }
MutexNode *right() const { return _right; }
void lock() { _mutex.lock(); }
void unlock() { _mutex.unlock(); }
private:
Mutex _mutex;
void *_obj;
MutexNode *_left, *_right;
};
MutexNode::MutexNode(void *o): _mutex(), _obj(o) {
_left = _right = 0;
}
MutexNode::~MutexNode() {
delete _left;
delete _right;
}
The Mutex Tree
The nodes are maintained in a tree - here is the typical recursive
descent algorithm, and so on. Note also the
method MutexTree::lock() which locks the entire tree before
applying a lock to a singular node.
class MutexTree {
public:
MutexTree();
~MutexTree();
void lock(void *o);
void unlock(void *o);
private:
void locktree() { _treelock.lock(); };
void unlocktree() { _treelock.unlock(); }
MutexNode *nodelock(void *o, MutexNode *start);
void nodeunlock(void *o, MutexNode *start);
MutexNode *_root;
Mutex _treelock;
};
MutexTree::MutexTree(): _treelock() {
_root = 0;
}
MutexTree::~MutexTree() {
delete _root;
}
void MutexTree::lock(void *o) {
locktree();
_root = nodelock(o, _root);
unlocktree();
}
void MutexTree::unlock(void *o) {
nodeunlock(o, _root);
}
MutexNode *MutexTree::nodelock(void *o, MutexNode *start) {
if (!start) {
start = new MutexNode(o);
start->lock();
} else if (start->obj() == o)
start->lock();
else if (start->obj() < o)
start->left(nodelock(o, start->left()));
else
start->right(nodelock(o, start->right()));
return start;
}
void MutexTree::nodeunlock(void *o, MutexNode *start) {
if (!start)
return;
if (start->obj() == o)
start->unlock();
else if (start->obj() < o)
nodeunlock(o, start->left());
else
nodeunlock(o, start->right());
}
Translating to mutex_lock() and mutex_unlock()
The class MutexTree is sufficient to implement the two
classless functions shown at the top of this post. All we need is one
static MutexTree to operate on:
static MutexTree mt;
void mutex_lock(void *obj) {
mt.lock(obj);
}
void mutex_unlock(void *obj) {
mt.unlock(obj);
}
Test Harnass
Plus of course, here's the test...
void *test(void *data) {
for (int i = 1; i <= 10; i++) {
if (i & 1) {
mutex_lock(&cout);
cout << pthread_self() << " testing cout, loop " << i << '\n';
mutex_unlock(&cout);
} else {
mutex_lock(&cerr);
cerr << pthread_self() << " testing cerr, loop " << i << '\n';
mutex_unlock(&cerr);
}
}
return 0;
}
int main() {
try {
pthread_t th, threads[10];
for (unsigned i = 0; i < sizeof(threads) / sizeof(pthread_t); i++) {
pthread_create(&th, 0, test, 0);
threads[i] = th;
}
for (unsigned i = 0; i < sizeof(threads) / sizeof(pthread_t); i++)
pthread_join(threads[i], 0);
cout << "Done!\n";
return 0;
} catch (char const *s) {
cerr << s << "\n";
return 1;
}
}
 | Update: I've modified the above version for faster
access - which can be useful in programs that use up a lot of
mutexes. The modified version has a hash-table, which indexes
trees. The first lookup of a mutex given a void* is
therefore much faster. Drop me a
note if you are interested.
The hash-table version wins performance-wise in a worst-case
scenario where loads of mutexes are being used, all relating to
linearly situated objects. E.g, consider:
int obj[5000];
for (int i = 0; i < 5000; i++)
mutex_lock(&obj[i]);
This is the worst case, in the sense that each consecutive object
to lock is (pointer-wise) 'larger' than the previous. The binary
tree of MutexTree will degenerate into a list. That's where the
hash table is beneficial.
However, most programs (a) won't have that many mutexes, and
(b) won't suffer from such linearity. I think therefore that in
most situations the binary tree approach will do quite nicely. |
|
|
|
 | 2010-03-20 Weird Eyechart |
 | 2010-03-15 Microproxy 1.01 |
 | 2010-03-05 Microproxy |
 | 2010-03-03 Sven Kramer and the wrong lane |
 | 2010-02-26 Endearing Babe Magnet |
 | 2010-02-17 Speed of light measured using chocolate and a microwave |
 | 2010-02-17 Never again expires after 65 years |
 | 2010-02-16 encfs on the Mac |
 | 2010-02-15 Hyves.nl and sexual predators |
 | 2010-02-10 Funny textbook |
 | 2010-02-09 DNS failing after sleep wake cycle |
 | 2010-02-06 Blast from the past |
 | 2010-01-28 Simple and straight Perl HTTP::Proxy |
 | 2010-01-15 Avatar the Movie |
 | 2010-01-08 Slightly NSFW Linux Ad |
 | 2010-01-07 WTF |
 | 2010-01-05 Stop Software Patents in the EU |
 | 2009-12-05 HammerServer 1.02 |
 | 2009-11-28 Perls Automagical Autoloading |
 | 2009-10-07 Office Poster |
 | 2009-10-06 The nr 1 Nerdjoke |
 | 2009-10-04 WoW Startscript for my Mac |
 | 2009-09-27 HammerServer section is online |
 | 2009-09-26 The BING HQ |
 | 2009-09-26 Digging a WOW Tunnel |
 | 2009-06-29 Wee Todd |
 | 2009-06-23 The On Off Switch Revisited |
 | 2009-06-22 Meatspace |
 | 2009-05-30 My old houses |
 | 2009-05-11 LOLcats are funny |
 | 2009-05-11 Civic Duty WIN |
 | 2009-05-10 Vote for the baby, Sky Radio promo FAIL |
 | 2009-05-05 My secure data center |
 | 2009-02-15 My Valentine is sending me a dot exe |
 | 2009-02-05 MacPorts trash: .mp_123456 savefiles cleaning |
 | 2009-02-01 Truecrypt 6 on Linux and the ext3 filesystem |
 | 2009-01-28 www versus nl.youtube.com |
 | 2009-01-27 Songsmith and The Police |
 | 2009-01-25 My own Ministery of Silly Walks |
 | 2009-01-09 CoolIris Mini HOWTO |
 | 2008-11-04 UDP and DNS balancing |
 | 2008-11-02 Life in graphs |
 | 2008-11-01 Skeined yet? |
 | 2008-10-30 New Crossroads on the horizon |
 | 2008-10-28 Thread safe or not |
 | 2008-10-15 WOW patch 3 on a case sensitive MacOSX filesystem |
 | 2008-10-15 Surprising C++ optimizations |
 | 2008-10-14 Weird system message |
 | 2008-10-08 Data mining against terrorism does not work |
 | 2008-09-16 Crossroads at the top of Freshmeat.net |
 | 2008-09-09 Stupid spammers at Computable |
 | 2008-09-06 Spam prevention with Postfix and Postgrey |
 | 2008-09-03 The Gnomish Flying Machine |
 | 2008-08-27 Bank customer data on eBay |
 | 2008-08-26 Mutexes in C++ Threads |
 | 2008-08-22 4M dataloss in the UK last year |
 | 2008-08-21 Dropping spam with Postfix and Spamassassin |
 | 2008-08-18 Bayes and the War on Photography |
 | 2008-08-13 Good marital advice |
 | 2008-08-12 Squid proxy for personal usage |
 | 2008-08-11 Posix threads in C++ |
 | 2008-08-09 Crossroads mailing list |
 | 2008-08-08 Crossroads 2.00 is out |
 | 2008-08-01 Fail Pics |
 | 2008-07-14 The Fish Dance |
 | 2008-07-01 Big Bother and Massive Data Storage |
 | 2008-06-30 MMV One of omitted Unix tools |
 | 2008-06-08 Even anonymous breadcrumbs can give you away |
 | 2008-05-29 Crossroads in Argentina |
 | 2008-05-20 The Party at the Company Outing |
 | 2008-05-19 Crossroads 1.80 is out |
 | 2008-05-18 Where does technical innovation really come from |
 | 2008-05-16 Corporate bs generator |
 | 2008-05-15 Even the Vatican has to adapt |
 | 2008-05-12 Big Brother is watching your dog |
 | 2008-05-09 666 all over the place |
 | 2008-04-17 Security and privacy are incompatible |
 | 2008-04-16 The Hallmark E Card |
 | 2008-04-15 Crosroads Solaris port is out |
 | 2008-04-04 Identity theft can cost you dearly |
 | 2008-04-03 Crossroads can already do that |
 | 2008-03-31 A dagerous safari |
 | 2008-03-28 Why some Java J2EE projects are inefficient |
 | 2008-03-26 The Hummingbird |
 | 2008-03-25 The Easter delusion |
 | 2008-03-18 McAfee detects mass hack of 200.000 webpages |
 | 2008-03-17 More predictive statistics |
 | 2008-03-10 Backwards conclusions even on Slashdot |
 | 2008-02-18 A fractal photograph |
 | 2008-02-15 Kaprekar revisited |
 | 2008-02-14 Kaprekar numbers |
 | 2008-02-12 A tale of the criminal ineptitude |
 | 2008-02-10 Irritating Selfregistered users in PHPBB |
 | 2008-02-08 B2B Spam in the Netherlands |
 | 2008-02-06 Surprising iSight Capture |
 | 2008-02-05 Breadcrumbs at WickedLasers.com |
 | 2008-01-29 iSight Capture Utility |
 | 2008-01-28 The Male Brain |
 | 2008-01-26 Searching for the next Uri Geller |
 | 2008-01-24 Opt in for b2b spam |
 | 2008-01-14 Bokito Revisited |
 | 2008-01-13 Top Crossroads User |
 | 2008-01-12 World of Warcraft Dancing |
 | 2008-01-12 Justice dispensed better late than never |
 | 2008-01-11 Jeremy Clarkson and Identity Theft |
 | 2008-01-10 Terrorism in the Netherlands |
 | 2007-12-07 The mind and bodysnatchers are among us |
 | 2007-12-05 Bruce Schneier and Hildo |
 | 2007-12-04 Bye bye, good Christian soul |
 | 2007-12-03 Confusing mail message |
 | 2007-11-30 Medion MD 85276 reviewed |
 | 2007-11-29 Recent cases of data exposure |
 | 2007-11-20 Bayes bites |
 | 2007-11-19 Japan starts fingerprinting foreigners |
 | 2007-11-14 Privacy, Yahoo and the Strange World |
 | 2007-11-14 Privacy, Fall through algorithms, and Securing data |
 | 2007-11-07 European airlines to retain data |
 | 2007-11-03 BloggEd |
 | 2007-10-30 Wilders and Marktplaats.nl |
 | 2007-10-28 The goldplated Mac |
 | 2007-10-26 More morons |
 | 2007-10-26 Dilbert nails it again |
 | 2007-10-23 Rough yet funny |
 | 2007-10-05 Another silly Trojan mail |
 | 2007-10-01 So ugly it is beautiful |
 | 2007-09-28 Here is a nickel kid |
 | 2007-09-23 Spy Shredder |
 | 2007-08-29 Web svn view 1.08 |
 | 2007-08-24 Caught in THE Process |
 | 2007-08-21 Stupid Trojan attack |
 | 2007-08-21 Back in 1994 |
 | 2007-08-20 A girly iPod |
 | 2007-08-17 Crossroads for RDP connections |
 | 2007-08-15 Firewall art |
 | 2007-08-14 jpeginfo |
 | 2007-08-13 Good People |
 | 2007-08-07 The Real Crossroads |
 | 2007-07-30 BBC Documentaries in the Netherlands |
 | 2007-07-12 No problems with Crossroads so far |
 | 2007-07-11 Politically correct ad nauseam |
 | 2007-07-02 Waka Waka Poem |
 | 2007-07-02 Voyage of the rubber ducks |
 | 2007-06-28 The On Off Switch |
 | 2007-06-27 No free lunch |
 | 2007-06-25 Crossroads web interface |
 | 2007-06-25 Blinkenlights |
 | 2007-06-21 There is no silver bullet |
 | 2007-06-18 Motto of the week |
 | 2007-06-18 Do not feed the troll |
 | 2007-06-17 Which programming language are you |
 | 2007-06-13 Crossroads support request |
 | 2007-06-12 Bokito glasses |
 | 2007-06-07 Apache mod_proxy balancer description |
 | 2007-06-05 A ticketnumber is not support |
 | 2007-06-05 403 Hammertime |
 | 2007-06-04 Playground Fun |
 | 2007-05-24 Ascii man |
 | 2007-05-07 Cannot find the damn server |
 | 2007-05-02 The BFG200 |
 | 2007-04-27 Crossroads Top User |
 | 2007-03-30 Crossroads Usage |
 | 2007-03-25 The guy with the dark motorhelmet |
 | 2007-03-22 The Process and The Result |
 | 2007-03-21 Quotes attributed to Jos |
 | 2007-03-20 A really nice comment about Crossroads |
 | 2007-03-18 Kubat in the air |