<div dir="ltr">
<div>Hi Lennart,</div><div><br></div><div>Thanks a lot for your elaborate reply. See inline replies...<br></div><div><br></div><div><span class="gmail-im">On Tue, Mar 5, 2019 at 11:27 AM Lennart Poettering <<a href="mailto:lennart@poettering.net" target="_blank">lennart@poettering.net</a>> wrote:<br>><br>> On Mo, 04.03.19 21:56, Stanislav Angelovič (<a href="mailto:angelovic.s@gmail.com" target="_blank">angelovic.s@gmail.com</a>) wrote:<br>><br>><br>> sd-bus doesn't natively care for threads. However it's written in kind<br>> of a threads-aware style, i.e. it keeps only local state, no global<br>> variables. This means you can do locking around it yourself and thus<br>> make it usable from multiple threads.</span></div><div><br></div><div>Good design, IMO.<br></div><span class="gmail-im"><div><br></div><div>><br>> To do that, allocate a single mutex of some form covering the sd_bus<br>> object and all sd_bus_message associated with it. You need to take the<br>> lock when you enter any sd_bus_xyz call, and release it after. To<br>> ensure that you can use sd-bus APIs from one thread while another one<br>> poll()s on the connection make sure that you do not use sd-bus' own<br>> polling (i.e. do not use sd_bus_wait(), since that would mean we'd<br>> have to wait for IO with the lock taken). Instead, use your own poll()<br>> loop that uses sd_bus_get_fd(), sd_bus_get_events(),<br>> sd_bus_get_timeout() to figure out what to wait for, and then unlock<br>> the mutex before poll() and lock it again after.<br>></div><div><br></div></span><div>Yes, we have played with an approach along these lines in sdbus-c++ recently.</div><div><br></div><div>But,
 looking at sd-bus code, perhaps not all operations on both bus and 
messages need a mutual lock. If in our library design, if we ensure that
 the bus is running properly, then it seems we can e.g. write output 
arguments to a method reply message (provided that the message is 
accessed only in this one thread) without locking access to the entire 
bus and other messages.<br></div><span class="gmail-im"><div><br></div><div>> Note that DBus puts an emphasis on message ordering, which means<br>> worker-thread-pool based dispatching is kinda incompatible with what<br>> it is built for (as thread pool stuff usually means out-of-order<br>> dispatching). Hence, whether doing threaded sd-bus is a good idea is a<br>> different question to ask.</div><div><br></div></span><div>The
 only specific thing in our worker-thread approach here is that when a 
D-Bus object receives a call of its method, it doesn't guarantee that it
 sends the reply before accepting another call of that method. If it 
receives calls c1 and c2 that order, it might e.,g, reply to c2 first, 
then to c1. On the caller side, the caller abstracts away from this and 
it still normally waits for the reply to its issued call. Is that 
against D-Bus message ordering rules?<br></div><span class="gmail-im"><div><br></div><div>><br>> sd-bus itself tries to push people towards a different model btw: that<br>> you have a separate bus connection for each thread<br>> (i.e. sd_bus_default() and friends tells you the default connection<br>> for your specific thread), and that within that thread you have perfect<br>> ordering.</div><div><br></div></span><div>I see. However, each connection then must have a different bus name. They cannot share the same bus name...<br></div><span class="gmail-im"><div><br></div><div>><br>> > 1. In sd_bus_message_handler, create a deep copy of the method call<br>> > sd_bus_message via sd_bus_message_copy(). And pass a pointer to this copy<br>> > to a worker thread. This is very straight-forward and simple, it solves the<br>> > race condition, but introduces a performance cost of copying (I don't know<br>> > how big this cost is, perhaps it's rather negligible).<br>><br>> It's fine to pass the sd_bus_message to worker threads, as long as you<br>> make sure you always have a reference to it. While passing the<br>> reference you don't have to lock the message/bus, but when accessing<br>> the message you have to take the lock however.</div><div><br></div></span><div>Taking
 the lock on our side is not sufficient, still. Because sd-bus accesses 
the message, too -- it unrefs it after method callback, without taking 
any lock. So we still have race condition.</div><div><br></div><div>Creating
 a message copy (to which sd-bus no more keeps reference, just we) and 
then passing this copy to a different thread seems a solution to me. 
With one drawback -- making the copy.<br></div><span class="gmail-im"><div><br></div><div>><br>> > 3. Solution on sd-bus side :-) which is to make sd_bus_message ref count<br>> > handling thread-safe (atomic), just like one for sd_bus. This avoids the<br>> > need for deep copy. What do you think? Anyway, we'd still have to come up<br>> > with a solution now, based on already releases sd-bus versions.<br>><br>> Given the fact that DBus as it stands now isn't really meant for<br>> processing msgs in worker thread pools I don't think we want to add<br>> more thread support to sd-bus. Doing worker thread pools for DBus can<br>> work in special cases but not in the general case (example: if you<br>> create an object in another service dbus gives you the guarantee that<br>> the method reply and the signal announcing the object arrive in a<br>> specific order, which is a property that cannot be held up if worker<br>> thread pools are used), hence I am very conservative with supporting<br>> this beyond how we already do (i.e that we maintain no global state,<br>> and thus allow you to apply locking yourself)</div><div><br></div></span><div>I understand your stance with throwing more threading support in sd-bus, and agree with it.<br></div><div><br></div><div>But,
 on the other hand, just changing the message ref_count++ and 
ref_count-- to atomic operations is truly a simple thing that doesn't 
involve broader change of paradigm. Sd-bus already does it this way -- 
for sd_bus ref count handling.<br></div><div><br></div><div>Having 
atomic message ref counting would open the doors for sd-bus users to 
"move" (not copy) messages from method callbacks to different threads, 
since, as mentioned above, throwing in locks on user's side is anyway 
not sufficient.</div>

<br>><br>> Lennart<br>><br>> --<br>> Lennart Poettering, Red Hat</div>