This Week in D May 21, 2017

Welcome to This Week in D! Each week, we'll summarize what's been going on in the D community and write brief advice columns to help you get the most out of the D Programming Language.

The D Programming Language is a general purpose programming language that offers modern convenience, modeling power, and native efficiency with a familiar C-style syntax.

This Week in D has an RSS feed.

This Week in D is edited by Adam D. Ruppe. Contact me with any questions, comments, or contributions.

Statistics

In the community

Community announcements

See more at the announce forum.

Tip of the Week

Contributed by ketmar.

Emulating synchronized(obj) call.

Imagine that you have a nice thread-safe... something. Like file abstraction, for example:

  class XFile {
    private SomeHiddenClass fileImpl;

    void read (void* ptr, size_t count) {
      synchronized(fileImpl) {
        fileImpl.non_thread_safe_read(ptr, count);
      }
    }

    void write (const(void)* ptr, size_t count) {
      synchronized(fileImpl) {
        fileImpl.non_thread_safe_write(ptr, count);
      }
    }
  }

And you want to add something like LockedWriter, so you will be able to do:

  XFile fl;
  ...
  {
    import std.format : formattedWrite;
    auto wr = fl.lockedWriter();
    // here, fl should be locked with `synchronize(fl.fileImpl)`
    formattedWrite(wr, "%s %s!", "hi", "there");
  }

Such thing may be useful for formattedWrite(), for example, so your writef() implementation won't be interrupted midway by another thread. But the problem is that synchronized() is the built-in, and you can't separate it to "lock" and "unlock" parts. Likely. But compiler does locking and unlocking under the hood, so it *should* be possible! And it is really possible: you just have to import some hidden druntime functions, and make your hands dirty. Let's do the trick:

  extern(C) void _d_monitorenter (Object h) nothrow; // magic import
  extern(C) void _d_monitorexit (Object h) nothrow; // magic import

  auto lockedWriter (XFile fl) {
    static struct LockedWriterImpl {
      private XFile fl;

      private this (XFile afl) nothow {
        _d_monitorenter(fl.fileImpl); //HERE! emulate `synchronized(fl.fileImpl)` enter
      }

      // postblit: just "enter" one more time, compiler will balance dtor calls
      // we are lucky: `synchronized()` mutex is reentrant
      this (this) nothow {
        _d_monitorenter(fl.fileImpl);
      }

      ~this () nothow {
        _d_monitorexit(fl.fileImpl); //HERE! emulate `synchronized(fl.fileImpl)` exit
      }

      // call underlying thread-unsafe implementation
      void put (const(char)[] s...) { fl.fileImpl.write(s.ptr, s.length); }
    }
    return LockedWriterImpl(fl);
  }

What is going on here? Internally, each object has hidden field named "monitor" (this is not a real name, you cannot access the field by this name!). It is used to implement sychronized() locks.

When compiler sees synchronized(obj), it actually generates a code like this:

  try {
    _d_monitorenter(obj);
    ...your code here...
  } finally {
    _d_monitorexit(obj);
  }

So we can emulate synchronized() call by doing the very same thing! And that's what LockedWriterImpl does: calling "enter" and "exit" functions directly, by importing them from druntime. As those functions are hidden too, we have to using the trick: we are declaring external C function, and let the linker do the rest.

IMPORTANT NOTE: current LockedWriterImpl is just a sample code. You'd better do at least some error checking there!

Learn more about D

To learn more about D and what's happening in D: