Boost logo

Boost :

From: Andrey Semashev (andysem_at_[hidden])
Date: 2007-08-26 15:30:14


Hello Tobias,

Sunday, August 26, 2007, 1:37:17 PM, you wrote:

>> Hmm, I'm not sure of the purpose of this project. Is it supposed to
>> pass several tools under its umbrella to boost via fast-track review?

> Sort of. It's just an idea, so far.

> Its purpose is to avoid lots of fast-track reviews (and reviewing
> overhead) for utility components by grouping them into a "pseudo
> library", thus encouraging developers to brush up / factor out useful
> stuff.

In this particular case the tool will be reviewed during the Boost.FSM
review (if it will, since it's not for public use anyway), so
including it in X-Files won't reduce the amount of reviews. On the
other hand, if Boost.FSM is rejected but there is interest to this
tool, I would gladly extract it to the X-Files project.

>> Yes, but consider that this code will be executed only once. The rest
>> of the execution time this mutex is useless.

> Consider the deadlock if 'once' is used recursively to initialize
> different resources...

The mutex is recursive.

> Further, it's quite unintuitive that a trivial initialization might get
> slowed down by one in another thread that takes a lot of time.

> You can call 'pthread_mutex_destroy' once you're done with the mutex to
> free up eventually acquired system resources.

I'll think of it. The first thing that comes to mind is that I'd have
to count threads that are hanging locked in the mutex since destroying
it right away would leave those threads in undefined behavior.

>>> Also, some platforms will not call 'mutex_destroyer' within a dynamic
>>> library (you probably know)...
>>
>> No, I'm not aware of this. Could you elaborate, please? Which
>> platforms are those?

> No ctors/dtors are run in static context for shared libraries on most
> UNIX platforms.

That's quite a surprise for me. I didn't encounter such behavior on
Linux (Red Hat). Do you have any workaround for this? I'm thinking of
GCC-specific attributes for this purpose, but that's one step away
from portability.

>> The fundamental problem arises here - I need to safely create a
>> synchronization object. Non-POSIX APIs don't provide things like
>> PTHREAD_MUTEX_INITIALIZER or I didn't find them in the docs.

> I see. Would it be an option to use 'yield' instead of 'sleep'?

The "yield" function is not guaranteed to switch execution context, it
may return immediately. If a lower-priority thread entered once
functor, you may spin for a relatively long time in a "yield" loop
instead of just letting the lower-priority thread finish its job.

>>> For some platforms (such as x86) memory access is atomic, so atomic
>>> operations are just a waste of time for simple read/write operations as
>>> the 'is_init' and 'set_called' stuff.
>>
>> The point is not only in atomic reads and writes, but in performing
>> memory barriers too. Otherwise the result of executing the once
>> functor could not have been seen by other CPUs.

> Then the memory barriers will suffice for x86, correct? As this code is
> executed on every call, any superfluous bus-locking should be avoided.

Actually, I got the impression that barriers themselves do a major
deal of performance impact. Besides, not all compilers support barrier
intrinsics.

> Alternatively, doing an "uncertain read" to check whether we might need
> initialization before setting up the read barrier might be close enough
> to optimal.

Well, that's a tricky point. I'm not an expert in threading issues,
but it's not obvious to me whether a memory barrier should act
regardless of its scope. For example:

void foo(int& x, int& y)
{
  if (x == 0)
  {
    read_memory_barrier();
    y = 10;
    x = 1;
    write_memory_barrier();
  }

  // use y
}

Now, is it guaranteed that those barriers are in effect regardless of
x value? I think not. Either the compiler may reorder statements in
such way that y is used before the "if" statement, or the same thing
may be done by CPU since the barrier instructions may not be executed.

>> Well, you may be right here. I could try to reduce memory allocations
>> in error handling.
>> But the only possible problem I see there is memory depletion. In such
>> case you'll get std::bad_alloc which adheres the declared interface of
>> the implementation. So, strictly speaking, if you have enough memory
>> you get a detailed error description. If not, you get bad_alloc.

> Depending on 'lexical_cast', 'iostream' and 'string' still slightly bugs
> me, though.

Ok, I'll change the code that formats the error string not to use
lexical_cast. But it will still depend on std::string since it's in
the exception class.

> Another potential issue: It seems Win32 and MacOS variants are currently
> not exception-safe. That is, the initialization routine isn't rerun if
> it has thrown the first time 'once' was called.

Yep, thanks for spotting that. I'll fix that in a couple of days and
update the library archive in the Vault. I'll post here a notification
when it's done.

-- 
Best regards,
 Andrey                            mailto:andysem_at_[hidden]

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk