It turns out that Boost is a wonderful library, and it code examples interspersed in the documentation may sometimes be broken. At least the one for boost::interprocess::named_mutex
in the docs is not functional on Windows systems.
*Always deleting a mutex as part of the demo code causes the mutex to not function. *
That should be commented in the demo code at the very least. It fails to pass the "principle of least amazement", although I wondered why it was there, I thought it must be idiomatic and necessary, it's idiotic and unnecessary, in actual fact. Or if it's necessary it's an example of what Joel Spolsky would call a leaky abstraction. If mutexes are really filesystem points under C:\ProgramData
in Windows I sure don't want to know about it, or know that turds get left behind that will break the abstraction if I don't detect that case and clean it up. (Sure smells like posix friendly semantics for mutexes in Boost have caused them to use a posix-style implementation instead of going to Win32 API directly and implementing a simple mutex that has no filesystem turds.)
Here's a working demo:
#include <windows.h>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <fstream>
#include <iostream>
#include <cstdio>
#include <windows.h>
int main (int argc, char *argv[])
{
srand((unsigned) time(NULL));
using namespace boost::interprocess;
try{
/*
// UNCOMMENT THIS IF YOU WANT TO MAKE THIS DEMO IMPOSSIBLE TO USE TO DEMO ANYTHING
struct file_remove
{
file_remove() { std::remove("file_name"); }
~file_remove(){ std::remove("file_name"); }
} file_remover;
// UNCOMMENT THIS IF YOU WANT TO BREAK THIS DEMO HORRIBLY:
struct mutex_remove
{
mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
} remover;
*/
//Open or create the named mutex
named_mutex mutex(open_or_create, "fstream_named_mutex");
std::ofstream file("file_name", std::ios_base::app );
int randomtime = 0;
for(int i = 0; i < 100; ++i){
//Do some operations...
//Write to file atomically
DWORD n1,n2;
n1 = GetTickCount();
{
scoped_lock<named_mutex> lock(mutex);
n2 = GetTickCount();
std::cout << "took " << (n2-n1) << " msec to acquire mutex";
randomtime = rand()%10;
if (randomtime<1)
randomtime = 1;
std::cout << " ... writing...\n";
if (argc>1)
file << argv[1];
else
file << "SOMETHING";
file << "...";
Sleep(randomtime*100);
file << " This is iteration #" << i;
file << std::endl;
file.flush();
}
Sleep(randomtime*100); // let the other guy in.
}
}
catch(interprocess_exception &ex){
std::cout << "ERROR " << ex.what() << std::endl;
return 1;
}
return 0;
}
I would love critques and edits on this answer, so that people will have a working demo of using this named mutex .
To use the demo:
- Build it and run two copies of it. Pass a parameter in so you can see which instance wrote which lines (start myexename ABC
and start myexename DEF
from a command prompt in windows)
- If it's your second run, delete any stray output named "file_name" if you don't want the second run appended to the first.