Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sketch of threads and signals #8

Open
o11c opened this issue Aug 9, 2016 · 5 comments
Open

Sketch of threads and signals #8

o11c opened this issue Aug 9, 2016 · 5 comments

Comments

@o11c
Copy link
Collaborator

o11c commented Aug 9, 2016

In order to allow updates without user intervention, it is critical to allow refresh within a signal handler. It would also be quite beneficial to allow all operations from multiple threads.

This is quite feasible if you're a little careful. The major limitations of signal handlers is that you're not allowed to call malloc, and you're not allowed to access any variables not marked volatile. Since we want threads, we might as well upgrade to std::atomic, which has a nice API, too.

Some operations need malloc, and thus must be done outside the signal handler:

  • Creating new tqdm instances (or destroying them).
  • Creating new tqdm_sink instances (I believe we want explicit control rather than recalculating from the file= stuff - and we definitely want separate _instances lists).
  • Changing any string variables, unless it is to a statically-allocated string - we could add a second API for that.

(On the other hand, we could do all memory allocation using mmap, which is allowed in a signal handler ...)

Operations that can be done in the signal handler:

  • Enabling or disabling a tqdm instance
  • refresh (including after update)
  • Possibly, writeing to a tqdm_sink (since this is just a refresh) - I haven't reasoned out all the collision logic yet for this case.

Each tqdm_sink contains an intrusive (to prevent extra memory allocations) doubly-linked (to allow removal - or perhaps we could use the indirect-pointer trick and singly-link it? I'm actually not sure about how much more effort is needed with the atomics part here - do we need spinlocks (which are problematic in signal handlers since the thread is no longer executing)?) list of active tqdm instances. This are added using the standard atomic CAS trick in a loop. We should also allow adding other sorts of permanent line.

If output is to a pipe(2), then writes of under PIPE_BUF (at least 512 - plenty for a typical terminal line) are guaranteed to be atomic. If we get EAGAIN, we just flag the current tqdm as dirty (replacing the Python implementation's no-implicit-refresh-within-N-seconds logic) and move on. If output is not to a pipe (or if lines are super long), we aren't guaranteed this behavior, but we should optimistically assume it will be so and fall back to a loop if we get a partial write. We will necessarily assume that no one else is writing to our fd.

If output is to a terminal or a TCP socket, we can call TIOCOUTQ to check if the buffer is getting full or not. This might allow even better decisions about whether to attempt a write or just mark it as dirty and wait until the next (SIGALRM or threaded) timed refresh.

Obviously, use of many of these features should be controllable by the user on a per-tqdm_sink basis - and probably also fully-disablable via macros. If we need C++98 compatibility, we'll have to use boost for atomics, threads, etc. We might also want to allow integration in someone else''s event loop, but I haven't thought out all the requirements and implications.

@CrazyPython
Copy link
Collaborator

@o11c This all seems bad for performance. Is there a C++ equivalent to the simple, no threading required, python signal handlers? Or maybe we can handle signals only in tqdm.update(). We also don't want to interfere with any existing SIGALRMS.

@o11c
Copy link
Collaborator Author

o11c commented Aug 9, 2016

For the typical case (SIGALRM or disabled auto-updates), no threads will be created by tqdm.

All that we're doing is allowing the user to call the library from any thread that they create.

As far as not interfering - we do have a choice of a couple different signals, but that's why it's an option anyway.

@CrazyPython
Copy link
Collaborator

@o11c would it result in a slowdown for cases where nobody uses threads? (also: misclicked there)

@CrazyPython CrazyPython added this to the Layout design milestone Aug 9, 2016
@CrazyPython
Copy link
Collaborator

@o11c We don't want to sacrifice serial speed for parallel speed - 90% of use cases will be serial.

@casperdcl
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants