pthreadpool: Add a test for the race condition fixed in the last commit
authorVolker Lendecke <vl@samba.org>
Wed, 29 Nov 2017 17:55:21 +0000 (18:55 +0100)
committerVolker Lendecke <vl@samba.org>
Sat, 9 Dec 2017 19:28:11 +0000 (20:28 +0100)
Bug: https://bugzilla.samba.org/show_bug.cgi?id=13179
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
lib/pthreadpool/tests.c

index 1aab80c2bb4284211b1cbd167b5e1d63896a4e0c..f0ae0aa4a93b1ec2ffe9df85ef768b3c8a968463 100644 (file)
@@ -308,6 +308,82 @@ static int test_busyfork(void)
        return 0;
 }
 
+static int test_busyfork2(void)
+{
+       struct pthreadpool_pipe *p;
+       pid_t child;
+       int ret, jobnum;
+       struct pollfd pfd;
+
+       ret = pthreadpool_pipe_init(1, &p);
+       if (ret != 0) {
+               fprintf(stderr, "pthreadpool_pipe_init failed: %s\n",
+                       strerror(ret));
+               return -1;
+       }
+
+       ret = pthreadpool_pipe_add_job(p, 1, busyfork_job, NULL);
+       if (ret != 0) {
+               fprintf(stderr, "pthreadpool_add_job failed: %s\n",
+                       strerror(ret));
+               return -1;
+       }
+
+       ret = pthreadpool_pipe_finished_jobs(p, &jobnum, 1);
+       if (ret != 1) {
+               fprintf(stderr, "pthreadpool_pipe_finished_jobs failed\n");
+               return -1;
+       }
+
+       ret = poll(NULL, 0, 10);
+       if (ret == -1) {
+               perror("poll failed");
+               return -1;
+       }
+
+       ret = pthreadpool_pipe_add_job(p, 1, busyfork_job, NULL);
+       if (ret != 0) {
+               fprintf(stderr, "pthreadpool_add_job failed: %s\n",
+                       strerror(ret));
+               return -1;
+       }
+
+       /*
+        * Do the fork right after the add_job. This tests a race
+        * where the atfork prepare handler gets all idle threads off
+        * the condvar. If we are faster doing the fork than the
+        * existing idle thread could get out of idle and take the
+        * job, after the fork we end up with no threads to take care
+        * of the job.
+        */
+
+       child = fork();
+       if (child < 0) {
+               perror("fork failed");
+               return -1;
+       }
+
+       if (child == 0) {
+               exit(0);
+       }
+
+       pfd = (struct pollfd) {
+               .fd = pthreadpool_pipe_signal_fd(p),
+               .events = POLLIN|POLLERR
+       };
+
+       do {
+               ret = poll(&pfd, 1, 5000);
+       } while ((ret == -1) && (errno == EINTR));
+
+       if (ret == 0) {
+               fprintf(stderr, "job unfinished after 5 seconds\n");
+               return -1;
+       }
+
+       return 0;
+}
+
 static void test_tevent_wait(void *private_data)
 {
        int *timeout = private_data;
@@ -423,6 +499,12 @@ int main(void)
                return 1;
        }
 
+       ret = test_busyfork2();
+       if (ret != 0) {
+               fprintf(stderr, "test_busyfork2 failed\n");
+               return 1;
+       }
+
        printf("success\n");
        return 0;
 }