wayland-client: abort when the reader_count is invalid and cancel read when a thread...
authorSung-Jin Park <sj76.park@samsung.com>
Thu, 31 Oct 2019 02:52:52 +0000 (11:52 +0900)
committerJunkyeong Kim <jk0430.kim@samsung.com>
Thu, 16 Feb 2023 10:10:19 +0000 (19:10 +0900)
display->reader_count must be equal or smaller than the number of threads
already prepared to read events. If it is larger than the number of threads,
most threads will wait for the last reader to wake them up, which can create
a blocking situation. In this case, we will do abort().

If a thread that was preparing to read the event(s) suddenly ends, we must
check if the reader_count_in_thread is greater than zero and call cancel_read()
if necessary. Otherwise, a blocking situation may occur.

Change-Id: Id496c83c244b03f97106d350b44b8798c9a6c0a9
Signed-off-by: Sung-Jin Park <sj76.park@samsung.com>
src/wayland-client.c

index e733299..cc8f07a 100644 (file)
@@ -115,6 +115,7 @@ struct wl_thread_data {
        thread_state state;
        pthread_t thread_id;
 // END
+       struct wl_display *display;
 };
 
 struct wl_display {
@@ -154,6 +155,7 @@ struct wl_display {
 // TIZEN_ONLY(20190716) : wayland-client : force sync of display when threads are waiting for over WL_PTHREAD_COND_TIMEDWAIT_TIMEOUT
        uint32_t force_sync_count;
 // END
+       uint32_t threads_count;
 };
 
 /** \endcond */
@@ -1158,10 +1160,19 @@ wl_proxy_marshal_array(struct wl_proxy *proxy, uint32_t opcode,
 static void
 destroy_thread_data(void *data)
 {
+       struct wl_display *display = NULL;
        struct wl_thread_data *thread_data = data;
 
+       display = thread_data->display;
+       assert(display);
+
        wl_list_remove(&thread_data->link);
-       wl_log("Thread removed[%p pid:%d tid: %d]\n", thread_data, thread_data->pid, thread_data->tid);
+       display->threads_count--;
+       wl_log("Thread removed[%p pid:%d tid: %d] from display:%p, threads_cnt=%d\n",
+               thread_data, thread_data->pid, thread_data->tid, display, display->threads_count);
+
+       if (thread_data->reader_count_in_thread > 0)
+               wl_display_cancel_read(display);
 
        free(thread_data);
 }
@@ -1181,12 +1192,15 @@ get_thread_data(struct wl_display *display)
 
                thread_data->pid = (int)getpid();
                thread_data->tid = (int)syscall(SYS_gettid);
+               thread_data->display = display;
 // TIZEN_ONLY(20190716) : wayland-client : force sync of display when threads are waiting for over WL_PTHREAD_COND_TIMEDWAIT_TIMEOUT
                thread_data->state = WL_THREAD_STATE_INITIAL;
                thread_data->thread_id = pthread_self();
 // END
                wl_list_insert(&display->threads, &thread_data->link);
-               wl_log("Thread added[%p, pid:%d tid: %d] to display:%p\n", thread_data, thread_data->pid, thread_data->tid, display);
+               display->threads_count++;
+               wl_log("Thread added[%p, pid:%d tid: %d] to display:%p, threads_cnt=%d\n",
+                       thread_data, thread_data->pid, thread_data->tid, display, display->threads_count);
        }
 
        return thread_data;
@@ -1422,6 +1436,7 @@ wl_display_connect_to_fd(int fd)
        if (display->connection == NULL)
                goto err_connection;
 
+       display->threads_count = 0;
        wl_list_init(&display->threads);
        if (pthread_key_create(&display->thread_data_key, destroy_thread_data) < 0)
                goto err_connection;
@@ -1866,6 +1881,38 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
        destroy_queued_closure(closure);
 }
 
+static int
+_check_reader_counts(struct wl_display *display)
+{
+       struct wl_thread_data *thread_data;
+       struct wl_thread_data *th_data, *th_data_next;
+
+       int cnt = 0;
+       int threads_reader_counts = 0;
+
+       thread_data = get_thread_data(display);
+       assert(thread_data);
+
+       wl_log("[check_reader_counts] Number of threads=%d, reader_count=%d\n", display->threads_count, display->reader_count);
+
+       wl_list_for_each_safe(th_data, th_data_next, &display->threads, link) {
+               wl_log("... thread[%d]:reader_count_in_thread=%d\n", cnt++, th_data->reader_count_in_thread);
+
+               if (th_data->reader_count_in_thread > 0)
+                       threads_reader_counts++;
+       }
+
+       if (threads_reader_counts < display->reader_count)
+       {
+               wl_log("[check_reader_counts] Abnormal  reader_count !\n");
+               wl_log("... reader_count=%d, total reader counts of threads=%d\n", display->reader_count, threads_reader_counts);
+
+               return 1;
+       }
+
+       return 0;
+}
+
 // TIZEN_ONLY(20190716) : wayland-client : force sync of display when threads are waiting for over WL_PTHREAD_COND_TIMEDWAIT_TIMEOUT
 static void
 log_threads_reader_info(struct wl_display *display)
@@ -1947,6 +1994,7 @@ read_events(struct wl_display *display)
        struct timespec ts;
        int ret = 0, res = 0;
 // END
+       uint32_t _force_sync_count = 0;
 
        thread_data = get_thread_data(display);
        assert(thread_data);
@@ -2054,11 +2102,27 @@ read_events(struct wl_display *display)
                                wl_log("[read events] display->read_serial : %d, serial : %d\n", display->read_serial, serial);
                                wl_log("=== Timeout on pthread_cond_timedwait. End of data leaving !===\n");
 
+                               /* After having waited for two times by calling pthread_cond_timedwait() and
+                                 * if the last reader (thread) doesn't wake the waiting threads up, it is considered to be an abnormal situation.
+                                 * Do wl_abort().
+                                 */
+                               if (_force_sync_count > 1 && display->read_serial == serial)
+                               {
+                                       if (_check_reader_counts(display))
+                                       {
+                                               if (!errno)
+                                                       errno = display->last_error;
+
+                                               wl_abort("Invalid reader count ! Abort !(reader_count=%d, errno=%d, last_error=%d)\n", display->reader_count, errno, display->last_error);
+                                       }
+                               }
+
                                thread_data->state |= WL_THREAD_STATE_FORCE_DISPLAY_SYNC_BEGIN;
                                wl_log("=== FORCE_DISPLAY_SYNC BEGIN ===\n");
 
                                res = force_display_sync(display);
                                display->force_sync_count++;
+                               _force_sync_count++;
 
                                thread_data->state |= WL_THREAD_STATE_FORCE_DISPLAY_SYNC_DONE;
                                wl_log("=== FORCE_DISPLAY_SYNC DONE (res=%d, force_sync_count=%d) ===\n", res, display->force_sync_count);