mirror of
				https://gitlab.freedesktop.org/wayland/wayland.git
				synced 2025-11-03 09:01:42 -05:00 
			
		
		
		
	client: update documentation about threading
Remove out-dated documentation and add few more words
about this topic.
v2. replace a paragraph by better explanation from Pekka Paalanen
    fix other notes from reviewing
v3. fix typo
v4. fix flags for poll in an example
    add wl_display_cancel_read() to another example
    (so that user sees that it should be used)
    move proper use of wl_display_prepare_read
    before the explanation why it is wrong to use
    wl_display_displach
Signed-off-by: Marek Chalupa <mchqwerty@gmail.com>
Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Daniel Stone <daniel@fooishbar.org>
			
			
This commit is contained in:
		
							parent
							
								
									77939736fb
								
							
						
					
					
						commit
						434fd45e4b
					
				
					 1 changed files with 116 additions and 36 deletions
				
			
		| 
						 | 
					@ -909,6 +909,12 @@ static const struct wl_callback_listener sync_listener = {
 | 
				
			||||||
 * Blocks until the server process all currently issued requests and
 | 
					 * Blocks until the server process all currently issued requests and
 | 
				
			||||||
 * sends out pending events on the event queue.
 | 
					 * sends out pending events on the event queue.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * \note This function uses wl_display_dispatch_queue() internally. If you
 | 
				
			||||||
 | 
					 * are using wl_display_read_events() from more threads, don't use this function
 | 
				
			||||||
 | 
					 * (or make sure that calling wl_display_roundtrip_queue() doesn't interfere
 | 
				
			||||||
 | 
					 * with calling wl_display_prepare_read() and wl_display_read_events())
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \sa wl_display_roundtrip()
 | 
				
			||||||
 * \memberof wl_display
 | 
					 * \memberof wl_display
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
WL_EXPORT int
 | 
					WL_EXPORT int
 | 
				
			||||||
| 
						 | 
					@ -940,6 +946,11 @@ wl_display_roundtrip_queue(struct wl_display *display, struct wl_event_queue *qu
 | 
				
			||||||
 * Blocks until the server process all currently issued requests and
 | 
					 * Blocks until the server process all currently issued requests and
 | 
				
			||||||
 * sends out pending events on the default event queue.
 | 
					 * sends out pending events on the default event queue.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * \note This function uses wl_display_dispatch_queue() internally. If you
 | 
				
			||||||
 | 
					 * are using wl_display_read_events() from more threads, don't use this function
 | 
				
			||||||
 | 
					 * (or make sure that calling wl_display_roundtrip() doesn't interfere
 | 
				
			||||||
 | 
					 * with calling wl_display_prepare_read() and wl_display_read_events())
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * \memberof wl_display
 | 
					 * \memberof wl_display
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
WL_EXPORT int
 | 
					WL_EXPORT int
 | 
				
			||||||
| 
						 | 
					@ -1216,14 +1227,59 @@ cancel_read(struct wl_display *display)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This will read events from the file descriptor for the display.
 | 
					 * This will read events from the file descriptor for the display.
 | 
				
			||||||
 * This function does not dispatch events, it only reads and queues
 | 
					 * This function does not dispatch events, it only reads and queues
 | 
				
			||||||
 * events into their corresponding event queues.  If no data is
 | 
					 * events into their corresponding event queues. If no data is
 | 
				
			||||||
 * available on the file descriptor, wl_display_read_events() returns
 | 
					 * available on the file descriptor, wl_display_read_events() returns
 | 
				
			||||||
 * immediately.  To dispatch events that may have been queued, call
 | 
					 * immediately. To dispatch events that may have been queued, call
 | 
				
			||||||
 * wl_display_dispatch_pending() or
 | 
					 * wl_display_dispatch_pending() or wl_display_dispatch_queue_pending().
 | 
				
			||||||
 * wl_display_dispatch_queue_pending().
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Before calling this function, wl_display_prepare_read() must be
 | 
					 * Before calling this function, wl_display_prepare_read() must be
 | 
				
			||||||
 * called first.
 | 
					 * called first. When running in more threads (which is the usual
 | 
				
			||||||
 | 
					 * case, since we'd use wl_display_dispatch() otherwise), every thread
 | 
				
			||||||
 | 
					 * must call wl_display_prepare_read() before calling this function.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * After calling wl_display_prepare_read() there can be some extra code
 | 
				
			||||||
 | 
					 * before calling wl_display_read_events(), for example poll() or alike.
 | 
				
			||||||
 | 
					 * Example of code in a thread:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \code
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   while (wl_display_prepare_read(display) < 0)
 | 
				
			||||||
 | 
					 *           wl_display_dispatch_pending(display);
 | 
				
			||||||
 | 
					 *   wl_display_flush(display);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   ... some code ...
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   fds[0].fd = wl_display_get_fd(display);
 | 
				
			||||||
 | 
					 *   fds[0].events = POLLIN;
 | 
				
			||||||
 | 
					 *   poll(fds, 1, -1);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   if (!everything_ok()) {
 | 
				
			||||||
 | 
					 *	wl_display_cancel_read(display);
 | 
				
			||||||
 | 
					 *	handle_error();
 | 
				
			||||||
 | 
					 *   }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   if (wl_display_read_events(display) < 0)
 | 
				
			||||||
 | 
					 *	handle_error();
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *   ...
 | 
				
			||||||
 | 
					 * \endcode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * After wl_display_prepare_read() succeeds, other threads that enter
 | 
				
			||||||
 | 
					 * wl_display_read_events() will sleep until the very last thread enters
 | 
				
			||||||
 | 
					 * it too or cancels. Therefore when the display fd becomes (or already
 | 
				
			||||||
 | 
					 * is) readable, wl_display_read_events() should be called as soon as
 | 
				
			||||||
 | 
					 * possible to unblock all threads. If wl_display_read_events() will not
 | 
				
			||||||
 | 
					 * be called, then wl_display_cancel_read() must be called instead to let
 | 
				
			||||||
 | 
					 * the other threads continue.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function must not be called simultaneously with wl_display_dispatch().
 | 
				
			||||||
 | 
					 * It may lead to deadlock. If programmer wants, for some reason, use
 | 
				
			||||||
 | 
					 * wl_display_dispatch() in one thread and wl_display_prepare_read() with
 | 
				
			||||||
 | 
					 * wl_display_read_events() in another, extra care must be taken to serialize
 | 
				
			||||||
 | 
					 * these calls, i. e. use mutexes or similar (on whole prepare + read sequence)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \sa wl_display_prepare_read(), wl_display_cancel_read(),
 | 
				
			||||||
 | 
					 * wl_display_dispatch_pending(), wl_display_dispatch()
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \memberof wl_display
 | 
					 * \memberof wl_display
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -1301,17 +1357,17 @@ wl_display_prepare_read_queue(struct wl_display *display,
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Prepare to read events after polling file descriptor
 | 
					/** Prepare to read events from the display's file descriptor
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \param display The display context object
 | 
					 * \param display The display context object
 | 
				
			||||||
 * \return 0 on success or -1 if event queue was not empty
 | 
					 * \return 0 on success or -1 if event queue was not empty
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This function must be called before reading from the file
 | 
					 * This function must be called before reading from the file
 | 
				
			||||||
 * descriptor using wl_display_read_events().  Calling
 | 
					 * descriptor using wl_display_read_events(). Calling
 | 
				
			||||||
 * wl_display_prepare_read() announces the calling threads intention
 | 
					 * wl_display_prepare_read() announces the calling thread's intention
 | 
				
			||||||
 * to read and ensures that until the thread is ready to read and
 | 
					 * to read and ensures that until the thread is ready to read and
 | 
				
			||||||
 * calls wl_display_read_events(), no other thread will read from the
 | 
					 * calls wl_display_read_events(), no other thread will read from the
 | 
				
			||||||
 * file descriptor.  This only succeeds if the event queue is empty
 | 
					 * file descriptor. This only succeeds if the event queue is empty
 | 
				
			||||||
 * though, and if there are undispatched events in the queue, -1 is
 | 
					 * though, and if there are undispatched events in the queue, -1 is
 | 
				
			||||||
 * returned and errno set to EAGAIN.
 | 
					 * returned and errno set to EAGAIN.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -1320,11 +1376,30 @@ wl_display_prepare_read_queue(struct wl_display *display,
 | 
				
			||||||
 * read intention by calling wl_display_cancel_read().
 | 
					 * read intention by calling wl_display_cancel_read().
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Use this function before polling on the display fd or to integrate
 | 
					 * Use this function before polling on the display fd or to integrate
 | 
				
			||||||
 * the fd into a toolkit event loop in a race-free way.  Typically, a
 | 
					 * the fd into a toolkit event loop in a race-free way.
 | 
				
			||||||
 * toolkit will call wl_display_dispatch_pending() before sleeping, to
 | 
					 * A correct usage would be (we left out most of error checking):
 | 
				
			||||||
 * make sure it doesn't block with unhandled events.  Upon waking up,
 | 
					 *
 | 
				
			||||||
 * it will assume the file descriptor is readable and read events from
 | 
					 * \code
 | 
				
			||||||
 * the fd by calling wl_display_dispatch().  Simplified, we have:
 | 
					 * while (wl_display_prepare_read(display) != 0)
 | 
				
			||||||
 | 
					 *         wl_display_dispatch_pending(display);
 | 
				
			||||||
 | 
					 * wl_display_flush(display);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * ret = poll(fds, nfds, -1);
 | 
				
			||||||
 | 
					 * if (has_error(ret))
 | 
				
			||||||
 | 
					 *         wl_display_cancel_read(display);
 | 
				
			||||||
 | 
					 * else
 | 
				
			||||||
 | 
					 *         wl_display_read_events(display);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * wl_display_dispatch_pending(display);
 | 
				
			||||||
 | 
					 * \endcode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Here we call wl_display_prepare_read(), which ensures that between
 | 
				
			||||||
 | 
					 * returning from that call and eventually calling
 | 
				
			||||||
 | 
					 * wl_display_read_events(), no other thread will read from the fd and
 | 
				
			||||||
 | 
					 * queue events in our queue. If the call to wl_display_prepare_read() fails,
 | 
				
			||||||
 | 
					 * we dispatch the pending events and try again until we're successful.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * When using wl_display_dispatch() we'd have something like:
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \code
 | 
					 * \code
 | 
				
			||||||
 * wl_display_dispatch_pending(display);
 | 
					 * wl_display_dispatch_pending(display);
 | 
				
			||||||
| 
						 | 
					@ -1333,8 +1408,8 @@ wl_display_prepare_read_queue(struct wl_display *display,
 | 
				
			||||||
 * wl_display_dispatch(display);
 | 
					 * wl_display_dispatch(display);
 | 
				
			||||||
 * \endcode
 | 
					 * \endcode
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * The race is immediately after poll(), where one thread
 | 
					 * This sequence in not thread-safe. The race is immediately after poll(),
 | 
				
			||||||
 * could preempt and read events before the other thread calls
 | 
					 * where one thread could preempt and read events before the other thread calls
 | 
				
			||||||
 * wl_display_dispatch(). This call now blocks and starves the other
 | 
					 * wl_display_dispatch(). This call now blocks and starves the other
 | 
				
			||||||
 * fds in the event loop.
 | 
					 * fds in the event loop.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -1346,26 +1421,20 @@ wl_display_prepare_read_queue(struct wl_display *display,
 | 
				
			||||||
 * before the other thread managed to call poll(), it will
 | 
					 * before the other thread managed to call poll(), it will
 | 
				
			||||||
 * block with events queued.
 | 
					 * block with events queued.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * A correct sequence would be:
 | 
					 * wl_display_prepare_read() function doesn't acquire exclusive access
 | 
				
			||||||
 * \code
 | 
					 * to the display's fd. It only registers that the thread calling this function
 | 
				
			||||||
 * while (wl_display_prepare_read(display) != 0)
 | 
					 * has intention to read from fd.
 | 
				
			||||||
 *         wl_display_dispatch_pending(display);
 | 
					 * When all registered readers call wl_display_read_events(),
 | 
				
			||||||
 * wl_display_flush(display);
 | 
					 * only one (at random) eventually reads and queues the events and the
 | 
				
			||||||
 * poll(fds, nfds, -1);
 | 
					 * others are sleeping meanwhile. This way we avoid races and still
 | 
				
			||||||
 * wl_display_read_events(display);
 | 
					 * can read from more threads.
 | 
				
			||||||
 * wl_display_dispatch_pending(display);
 | 
					 | 
				
			||||||
 * \endcode
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Here we call wl_display_prepare_read(), which ensures that between
 | 
					 | 
				
			||||||
 * returning from that call and eventually calling
 | 
					 | 
				
			||||||
 * wl_display_read_events(), no other thread will read from the fd and
 | 
					 | 
				
			||||||
 * queue events in our queue. If the call to wl_display_prepare_read() fails,
 | 
					 | 
				
			||||||
 * we dispatch the pending events and try again until we're successful.
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * If the relevant queue is not the default queue, then
 | 
					 * If the relevant queue is not the default queue, then
 | 
				
			||||||
 * wl_display_prepare_read_queue() and wl_display_dispatch_queue_pending()
 | 
					 * wl_display_prepare_read_queue() and wl_display_dispatch_queue_pending()
 | 
				
			||||||
 * need to be used instead.
 | 
					 * need to be used instead.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * \sa wl_display_cancel_read(), wl_display_read_events()
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * \memberof wl_display
 | 
					 * \memberof wl_display
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
WL_EXPORT int
 | 
					WL_EXPORT int
 | 
				
			||||||
| 
						 | 
					@ -1374,13 +1443,15 @@ wl_display_prepare_read(struct wl_display *display)
 | 
				
			||||||
	return wl_display_prepare_read_queue(display, &display->default_queue);
 | 
						return wl_display_prepare_read_queue(display, &display->default_queue);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Release exclusive access to display file descriptor
 | 
					/** Cancel read intention on display's fd
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \param display The display context object
 | 
					 * \param display The display context object
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This releases the exclusive access.  Useful for canceling the lock
 | 
					 * After a thread successfully called wl_display_prepare_read() it must
 | 
				
			||||||
 * when a timed out poll returns fd not readable and we're not going
 | 
					 * either call wl_display_read_events() or wl_display_cancel_read().
 | 
				
			||||||
 * to read from the fd anytime soon.
 | 
					 * If the threads do not follow this rule it will lead to deadlock.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \sa wl_display_prepare_read(), wl_display_read_events()
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \memberof wl_display
 | 
					 * \memberof wl_display
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -1420,6 +1491,9 @@ wl_display_cancel_read(struct wl_display *display)
 | 
				
			||||||
 * That means that this function can return non-0 value even when it
 | 
					 * That means that this function can return non-0 value even when it
 | 
				
			||||||
 * haven't dispatched any event for the given queue.
 | 
					 * haven't dispatched any event for the given queue.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * This function has the same constrains for using in multi-threaded apps
 | 
				
			||||||
 | 
					 * as \ref wl_display_dispatch().
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * \sa wl_display_dispatch(), wl_display_dispatch_pending(),
 | 
					 * \sa wl_display_dispatch(), wl_display_dispatch_pending(),
 | 
				
			||||||
 * wl_display_dispatch_queue_pending()
 | 
					 * wl_display_dispatch_queue_pending()
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -1525,11 +1599,17 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
 | 
				
			||||||
 * the appropriate event queues. Finally, events on the default event queue
 | 
					 * the appropriate event queues. Finally, events on the default event queue
 | 
				
			||||||
 * are dispatched.
 | 
					 * are dispatched.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * In multi-threaded environment, programmer may want to use
 | 
				
			||||||
 | 
					 * wl_display_read_events(). However, use of wl_display_read_events()
 | 
				
			||||||
 | 
					 * must not be mixed with wl_display_dispatch(). See wl_display_read_events()
 | 
				
			||||||
 | 
					 * and wl_display_prepare_read() for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * \note It is not possible to check if there are events on the queue
 | 
					 * \note It is not possible to check if there are events on the queue
 | 
				
			||||||
 * or not. For dispatching default queue events without blocking, see \ref
 | 
					 * or not. For dispatching default queue events without blocking, see \ref
 | 
				
			||||||
 * wl_display_dispatch_pending().
 | 
					 * wl_display_dispatch_pending().
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \sa wl_display_dispatch_pending(), wl_display_dispatch_queue()
 | 
					 * \sa wl_display_dispatch_pending(), wl_display_dispatch_queue(),
 | 
				
			||||||
 | 
					 * wl_display_read_events()
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * \memberof wl_display
 | 
					 * \memberof wl_display
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue