getOpt with shared

Jonathan M Davis newsgroup.d at jmdavisprog.com
Fri May 11 17:49:17 UTC 2018


On Friday, May 11, 2018 17:25:44 Danny Arends via Digitalmars-d-learn wrote:
> Hey all,
>
> I have been working on creating a multi-threaded application, so
> I have a shared configuration object which hold several command
> line parameters (which I fill using getopt).
>
> The problem is that I get deprecation warnings when trying to set
> numerical values:
>
> /usr/include/dmd/phobos/std/getopt.d(895,36): Deprecation:
> read-modify-write operations are not allowed for shared
> variables. Use core.atomic.atomicOp!"+="(*receiver, 1) instead.
>
> using getopt with a shared object and boolean values seems
> completely broken:
>
> /usr/include/dmd/phobos/std/getopt.d(895,34): Error: operation
> not allowed on bool *receiver += 1
> /usr/include/dmd/phobos/std/getopt.d(751,46): Error: template
> instance `std.getopt.handleOption!(shared(bool)*)` error
> instantiating
> /usr/include/dmd/phobos/std/getopt.d(435,15):        6 recursive
> instantiations from here: getoptImpl!(string, shared(string)*,
> string, shared(string)*, string, shared(VSync)*, string,
> shared(ulong)*, string, shared(bool)*, string, shared(Verbose)*)
>
> Is getopt not supposed to be used with shared structs ?

getopt is designed to be single-threaded. The keyword shared is not used a
single type in that module. If you want to use shared with anything in D,
you have three options:

1. Use core.atomic to atomically do stuff like increment a integer.

2. Use a mutex (or synchronized block) to protect access to a shared object.
You lock the mutex before accessing the object (and before _all_ accesses to
that object). Within that section of code, you cast away shared and operate
on the object as thread-local. Then at the end of that section, before
releasing the lock, you make sure that no thread-local references to the
shared object remain, and then free the lock. e.g. something like

synchronized(mutex)
{
    auto tls = cast(MyObject)mySharedObject.

    //... do stuff...

} // at this point, no thread-local references to mySharedObject
  // should // exist

In this scenario, the types in question are not designed to be used with
shared at all. They're designed to be thread-local. So, if they're marked as
shared, they're basically useless except when you protect them with a mutex
and correctly cast away shared in order to operate on the object while it's
protected by the mutex and therefore is thread-safe.

3. An object is designed to be used as shared. In this case, it can have
shared member functions, and they can be called on shared objects, but then
internally, the object has to deal with properly managing shared. It either
uses atomics and/or uses mutexes as in #2 - it's just that in this case,
it's done internally by the object rather than the programmer using the
object.

synchronized classes are supposed to help with this particular case, but
unfortunately, they're not fully implemented, so they don't currently help.
But either way, the concept is still the same. You have on object that is
designed to work with shared and deals with all of the appropriate
protections internally.

So, those are your three options. In the case of getopt, if you want to use
shared, you basically have to use #2. It's not dealing with a basic type
like an int or pointer, so atomics aren't going to work, and it's not
designed to work with shared. So, if you want to do anything with getopt and
shared, you're going to have to protect it with a mutex.

That being said, I have to say that getopt seems like a really weird choice
for wanting to do anything with shared. You normally call it immediately at
the beginning of the program before doing anything with threads. The results
might then be passed on to other threads via std.concurrency or through
shared variables, but I wouldn't think that it would make a lot of sense to
try and used getopt from more than one thread.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list