On Wed, Apr 03, 2013 at 01:19:31PM -0400, Kevyn-Alexandre Paré <kap...@rogue-research.com> wrote: > > As my past habits was to always create a thread when something requires > more processing time. I'm a bit confused to when to use thread with > libev and how to manage longer tasks. My first thought is that I have > started to use libev to remove the overhead of thread, context switching > and mutex.
Well, the major thing to keep in mind is that libev is "just" an event lib, and doesn't try to do anything else. It does, however, try to give you all the options, i.e., it provides ev_async watchers so you can signal it from other threads, and it provides the hooks required to add locking when that is deemed necessary. Indeed, you remove the thread switching overhead (which often involves the kernel and thus is higher than the overhead introduced by multiplexing between callbacks), and you don't need mutexes to start other thtreads and let them signal e.g. completion or other events, unless you touch shared data structures. In return, however, you will run into latency problems for long-running jobs. There are many ways to deal with this, and in ym experienc,e there is no best way. For example, a commonly overlooked method is to run multiple threads, and, when a callback "realises" that it takes longer, it can hand off the libev loop to another thread to continue, which gives more flexibility. If all you have is a few long running jobs and most things are fast, then starting threads in the background might be easier (and possibly fater as well). In perl, I often use cooperative (userspace) threads (using Coro, which uses libcoro) and IO::AIO (== libeio) together with EV (== libev). While perl is much slower than C, it is also way more convenient, often allowing me to express an algorithm that overall gives lower response time that I wouldn't bother implementing in C, because it would feel so anal. What I mean here is that often it might pay off to use some high level scripting language for program logic, if that allows me to express algorithms conveniently. (Of course, one can do the same in C, but without sugaring for features like closures it's often quite painful). Now, to come back to the background jobs - in perl's Coro for example, threads that run at a lower priority than the thread running the event loop will be time-multiplexed with event fetching, that is, when such a thread blocks or yields, the event loop runs to fetch new events and so on. The same effect can be had by having an idle watcher with a callback that does "some work" and then waits for the next idle event. OR by using some cooperative threads for C etc. etc. I am not recommending this, just giving some ideas. > 1- How long could I stay in a callback (ex: io watcher callback) and > process data until the rest of my system become non responsive (ex: > server)? Well, as long as it takes to beocme unresponsive. What is considered "unresponsive" depends heavily on the application. Libev sets some limits to responsiveness, for example, it doesn't guarantee a very high resolution for timers, and event processing can take an unbounded time in theory (proportional to the event load of course), but mostly, the responsiveness is limited by the callbacks. Obviously, if your callback is doing some blocking database lookup, or some disk access, you are in for a potentially very long wait. However, you can basically always know in advance whether an opertaion potentially takes a lot of time, and can take precautions by pushing it to the backgroud. > 2- Will it be preferable to simply start a thread if we know that the > processing could go higher to a certain threshold? If your threshhold exceeds the limit on responsiveness you want to endure, then starting a thread might help. Or using a threadpool, to reduce thread start overhead. Or pass libev to another thread, which makes it easier to only switch to another thread when needed, as you can dynamically decide whether to continue event processing or hand off event processing to another thread. > 3- How to determine that acceptable threshold? Well, if you have some medical machine and the patient dies if you don't respond within a second, then the acceptable threshold is lower than one second. In practise you usually don't have such hard limits, and all you want to do is make sure you don't have very long avoidable pauses. That means that, for the most part, there are some obviously unbounded operations, such as waiting fro the disk (which can take 0.01s, but can also take seconds when some other process does heavy disk activity, or even many minutes when linux thinks you suck and need to suffer), or waiting for a database (which might involve the disk), or waiting for data to arrive on a network socket. libev can help with the latter directly, and with the former ones indirectly, as long as your database library, I/O library etc. can work in an event-based fashion. > Do you have some thought on that subject? Lots :) But it's all down to experience (to gather), and mostly depends on the problem at hand. I would suggets starting either with having multiple threads and handing over libev to another thread before startign along operation, or using some existing threadpool (no recommendations here) and ev_async to signal completion, or simply by starting a new thread each time you need a long-running job, and seeing if that is fats enough for you. Of coruse, you can hardly avoid some mutexes and context switching when using threads, but at least you can reduce them to when they are really needed. -- The choice of a Deliantra, the free code+content MORPG -----==- _GNU_ http://www.deliantra.net ----==-- _ generation ---==---(_)__ __ ____ __ Marc Lehmann --==---/ / _ \/ // /\ \/ / schm...@schmorp.de -=====/_/_//_/\_,_/ /_/\_\ _______________________________________________ libev mailing list libev@lists.schmorp.de http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev