Example code in std.logger.core doesn't even work

Jonathan M Davis newsgroup.d at jmdavisprog.com
Wed Jul 12 23:11:00 UTC 2023


On Wednesday, July 12, 2023 10:32:22 AM MDT mw via Digitalmars-d wrote:
> On Wednesday, 12 July 2023 at 10:29:50 UTC, WebFreak001 wrote:
> > On Wednesday, 12 July 2023 at 03:00:11 UTC, mw wrote:
> >> On Monday, 13 March 2023 at 20:32:08 UTC, WebFreak001 wrote:
> >>> On Monday, 13 March 2023 at 02:53:00 UTC, Yuxuan Shui wrote:
> >>>> Example here:
> >>>>
> >>>> https://dlang.org/phobos/std_logger_core.html#.sharedLog
> >>>>
> >>>> Result:
> >>>>
> >>>> https://run.dlang.io/is/RMtF0I
> >>>
> >>> this changed in one of the most recent releases, but you can
> >>> always expect breaking changes in an std.experimental module
> >>
> >> Can you please show the correct usage pattern of this
> >> sharedLog, in the new release?
> >
> > what I do now in my code:
> >
> > ```d
> > static if (__VERSION__ < 2101)
> >
> >     sharedLog = new FileLogger(io.stderr);
> >
> > else
> >
> >     sharedLog = (() @trusted => cast(shared) new
> >
> > FileLogger(io.stderr))();
> > ```
> >
> > not a big fan of the changes and the fact that this got made
> > std.logger instead of the previous API, but it works at least
>
> 1) Is this `cast` ugly?
> 2) if it's a regular file, is this still thread safe to use?
>
>
> Without the `cast`, I got compiler error
>
> https://run.dlang.io/is/h3S0eb
> ```
> import std.stdio;
> import std.algorithm;
> import std.logger.core, std.logger.filelogger;
> import std.range;
>
> void main()
> {
>      // sharedLog = new shared(FileLogger)("/dev/null");
>
>      static if (__VERSION__ < 2101)
>   sharedLog = new FileLogger(io.stderr);
> else
>   sharedLog = (() @trusted => new shared(FileLogger)(stderr))();
>
> }
> ```
>
>
> onlineapp.d(13): Error: none of the overloads of `__ctor` are
> callable using a `shared` object
> /dlang/dmd/linux/bin64/../../src/phobos/std/logger/filelogger.d(40):
> Candidates are: `std.logger.filelogger.FileLogger.this(const(string) fn,
> const(LogLevel) lv = LogLevel.all)`
> /dlang/dmd/linux/bin64/../../src/phobos/std/logger/filelogger.d(66):
>                 `std.logger.filelogger.FileLogger.this(const(string) fn,
> const(LogLevel) lv, Flag createFileNameFolder)`
> /dlang/dmd/linux/bin64/../../src/phobos/std/logger/filelogger.d(105):
>                  `std.logger.filelogger.FileLogger.this(File file,
> const(LogLevel) lv = LogLevel.all)`
>
>
> Sigh, D is so broken on such basic stuff.

In general, objects are not supposed to be constructed as shared or even
really operated on as shared. A shared object is shared across threads, and
as long as that's done without protections in place, it's not thread-safe to
actually do anything with the object. So, in most cases, what you need to do
in order to operate on a shared object is to protect that section of code
with a mutex, cast away shared to get a thread-local reference to the
object, do whatever you need to do to the object while the mutex is locked,
and then before you release the mutex, you make sure that no thread-local
references to the object remain. That way, you don't ever operate on data
while it's actively been shared across threads. The compiler prevents you
from operating on the object through a shared reference precisely because
doing so would not be thread-safe. This does of course result in some
annoying casts, but it allows you to segregate the code that actually
operates on shared objects, and because the language has shared, it prevents
you from accidentally doing operations on shared objects that aren't
thread-safe as long as the code is @safe (since the casts to remove shared
have to be done in @trusted code to be callable by @safe code).

Outside of dealing with atomics, the only time that a shared object can be
operated on when it's still shared is when it's a type that's specifically
designed to work as shared. In that case, its member functions will be
marked with shared so that they can be called when the object is shared, and
instead of you casting away shared to do anything to the object, the mutexes
and casting and whatnot are all handled internally within the member
functions. But whether you're handling the mutexes and casting yourself, or
whether the object itself handles that, the data within the object can't be
operated one as long as it's shared (except for when using atomics).

Unfortunately, because most languages don't have shared in their type
systems (and don't have objects be thread-local by default), shared is often
misunderstood and misused.. And it is annoying to deal with, but that
annoyance is largely on purpose, because it's the type system attempting to
protect you from doing anything that isn't thread-safe. It would of course
be nice if the type system were sophisticated enough to be able to
automatically remove shared for you in some cases (e.g. because it knows
that you have already protected the shared object with a mutex in a
particular section of code), but it's difficult to give the compiler enough
information for it to be able to do that. So, as things stand, it's up to
the programmer to make sure that they've protected shared data appropriately
before casting away shared and operating on it as thread-local while it's
safe to do so.

Ultimately, you have to do basically the same stuff in languages like C or
C++, but in those languages, you don't have the compiler trying to prevent
you from doing anything that isn't thread-safe when it knows that data is
shared across threads. They just leave everything as shared and leave it up
to the programmer to deal with it all correctly. With D, you still have to
get it right in the sections of code where you cast away shared, but the
sections of code where that happens are then much better defined (since they
have to be @trusted if you're using @safe), and in @safe code, you can't
accidentally operate on shared data, because the compiler gives you an error
when you attempt it. So, we're arguably much better off, but there is
certainly some annoyance in the trade-off.

- Jonathan M Davis





More information about the Digitalmars-d mailing list