[uuids] generating uuids from multiple threads

I currently have a shared boost::uuids::basic_uuid_generator<boost::mt19937> which I protect by a boost::mutex when I generate a new uuid. The code looks something like: std::wstring GetUniqueName() { boost::uuids::uuid uniqueId; { boost::mutex::scoped_lock lock( my_mutex ); uniqueId = my_generator(); } return boost::lexical_cast<std::wstring>( uniqueId ); } For one thing this is yet another case where I used a default constructor on a uuid during natural coding and expected it to be cheap but I digress. Do I have to protect the shared generator with a mutex? Should I even be using a shared generator or should I be constructing it on the stack in this function? I don't expect this to be called very often (twice total in our current application) but I want to make sure it is bullet proof since it can potentially be called concurrently. -- Michael Marcin

On Fri, 09 Jan 2009 03:48:05 -0600, Michael Marcin <mike.marcin@gmail.com> wrote:
For one thing this is yet another case where I used a default constructor on a uuid during natural coding and expected it to be cheap but I digress.
Do I have to protect the shared generator with a mutex? Should I even be using a shared generator or should I be constructing it on the stack in this function? I don't expect this to be called very often (twice total in our current application) but I want to make sure it is bullet proof since it can potentially be called concurrently.
A PRNG is stateful, hence concurrent access lead need to be protected, but your mutex needs to be static, of course. I just had exactly the same need and I did protect it with a mutex. You will probably also have the need to do a "init once" for your PRNG. I did something like this for the init : static boost::hellekalek1995 rng; static boost::mutex mutex; static volatile bool initialized = false; if (!initialized) { // double checking pattern boost::lock_guard<boost::mutex> lock(mutex); if (!initialized) { // do the init rng.seed(blah blah blah); initialized = true; } } -- EA

Edouard A. wrote:
On Fri, 09 Jan 2009 03:48:05 -0600, Michael Marcin <mike.marcin@gmail.com> wrote:
For one thing this is yet another case where I used a default constructor on a uuid during natural coding and expected it to be cheap but I digress.
Do I have to protect the shared generator with a mutex? Should I even be using a shared generator or should I be constructing it on the stack in this function? I don't expect this to be called very often (twice total in our current application) but I want to make sure it is bullet proof since it can potentially be called concurrently.
A PRNG is stateful, hence concurrent access lead need to be protected, but your mutex needs to be static, of course.
I just had exactly the same need and I did protect it with a mutex. You will probably also have the need to do a "init once" for your PRNG.
I did something like this for the init :
static boost::hellekalek1995 rng; static boost::mutex mutex; static volatile bool initialized = false;
if (!initialized) { // double checking pattern boost::lock_guard<boost::mutex> lock(mutex); if (!initialized) { // do the init rng.seed(blah blah blah); initialized = true; } }
I'm not a threading guru but I was under the impression that double-checked locking pattern is dependent on the compiler and processor memory model and not portable. I am guaranteed that my function won't be called before main and no threads are started before main so I think that gives me some leeway for an easier solution. The uuids documentation says: "The boost::uuids::uuid_generator::operator() function returns a random-number-based uuid. The default random number generator is boost::mt19937 from the Boost Random library. It is seeded with a SHA-1 hash of a number of different values including std::time(0), std::clock(), uninitialized data, value return from new unsigned int, etc.." I'm not sure if this means I can expect to create a uuid_generator on the stack and get a uuid A and then create a uuid_generator on the stack and get uuid A != B. It seems to be that if somehow thread id or processor id was also used to create the seed the value I could do this and avoid any shared state and locking. -- Michael Marcin

On Fri, 09 Jan 2009 12:46:20 -0600, "Michael Marcin" <mike.marcin@gmail.com> said:
Edouard A. wrote:
On Fri, 09 Jan 2009 03:48:05 -0600, Michael Marcin <mike.marcin@gmail.com> wrote:
For one thing this is yet another case where I used a default constructor on a uuid during natural coding and expected it to be cheap but I digress.
Do I have to protect the shared generator with a mutex? Should I even be using a shared generator or should I be constructing it on the stack in this function? I don't expect this to be called very often (twice total in our current application) but I want to make sure it is bullet proof since it can potentially be called concurrently.
A PRNG is stateful, hence concurrent access lead need to be protected, but your mutex needs to be static, of course.
This is true. Thus a boost::uuids::uuid_generator is not thread safe and must be protected from concurrent access.
I just had exactly the same need and I did protect it with a mutex. You will probably also have the need to do a "init once" for your PRNG.
I did something like this for the init :
static boost::hellekalek1995 rng; static boost::mutex mutex; static volatile bool initialized = false;
if (!initialized) { // double checking pattern boost::lock_guard<boost::mutex> lock(mutex); if (!initialized) { // do the init rng.seed(blah blah blah); initialized = true; } }
I'm not a threading guru but I was under the impression that double- checked locking pattern is dependent on the compiler and processor memory model and not portable.
I am guaranteed that my function won't be called before main and no threads are started before main so I think that gives me some leeway for an easier solution.
The uuids documentation says:
"The boost::uuids::uuid_generator::operator() function returns a random-number- based uuid. The default random number generator is boost::mt19937 from the Boost Random library. It is seeded with a SHA-1 hash of a number of different values including std::time(0), std::clock(), uninitialized data, value return from new unsigned int, etc.."
I'm not sure if this means I can expect to create a uuid_generator on the stack and get a uuid A and then create a uuid_generator on the stack and get uuid A != B. It seems to be that if somehow thread id or processor id was also used to create the seed the value I could do this and avoid any shared state and locking.
It is expensive to create a boost::uuids::uuid_generator compared to generating a uuid from it. If you are creating lots of them, you may not want to create a generator every time you need to create a uuid. You can expect that different uuid_generators will produce different uuids, but it is possible that they will not. This is not likely since it is likely that they will be seeded with different seeds. This could be a good solution for you, that is create one uuid_generator per thread.
-- Michael Marcin
Regards, Andy

Andy Tompkins wrote:
I'm not a threading guru but I was under the impression that double- checked locking pattern is dependent on the compiler and processor memory model and not portable.
I am guaranteed that my function won't be called before main and no threads are started before main so I think that gives me some leeway for an easier solution.
The uuids documentation says:
"The boost::uuids::uuid_generator::operator() function returns a random-number- based uuid. The default random number generator is boost::mt19937 from the Boost Random library. It is seeded with a SHA-1 hash of a number of different values including std::time(0), std::clock(), uninitialized data, value return from new unsigned int, etc.."
I'm not sure if this means I can expect to create a uuid_generator on the stack and get a uuid A and then create a uuid_generator on the stack and get uuid A != B. It seems to be that if somehow thread id or processor id was also used to create the seed the value I could do this and avoid any shared state and locking.
It is expensive to create a boost::uuids::uuid_generator compared to generating a uuid from it. If you are creating lots of them, you may not want to create a generator every time you need to create a uuid.
You can expect that different uuid_generators will produce different uuids, but it is possible that they will not. This is not likely since it is likely that they will be seeded with different seeds. This could be a good solution for you, that is create one uuid_generator per thread.
How expensive is it in terms of memory to store a static or thread local uuid_generator for the length of the program? I only expect this to be called maybe twice. The application will run on memory constrained mobile devices. Of course if the code I make is not correct optimizing for size or speed doesn't matter. -- Michael Marcin

On Fri, Jan 9, 2009 at 19:11, Michael Marcin <mike.marcin@gmail.com> wrote:
How expensive is it in terms of memory to store a static or thread local uuid_generator for the length of the program? I only expect this to be called maybe twice. The application will run on memory constrained mobile devices.
The random-based generator is essentially just a wrapper on the (P)RNG, so it'll take as much storage as the PRNG. (v13 stores the variate generator too, but I think that'll change, since afaik that's cheap to construct.) There's a nice table with memory requirements here: http://www.boost.org/doc/libs/1_37_0/libs/random/random-generators.html#intr... So while the default PRNG needs 625*sizeof(uint32_t) of state, you could use a basic_uuid_generator<rand48>, which will need only 64 bits of storage. (Plus apparently at least 64 bits for the generator, but you can get rid of that easily enough by changing the uuid generator to construct it on demand.) ~ Scott
participants (4)
-
Andy Tompkins
-
Edouard A.
-
Michael Marcin
-
Scott McMurray