LCOV - code coverage report
Current view: top level - lib/pthreadpool - tests_cmocka.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 102 109 93.6 %
Date: 2024-04-21 15:09:00 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Unix SMB/CIFS implementation.
       3             :  * cmocka tests for thread pool implementation
       4             :  * Copyright (C) Christof Schmitt 2017
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include <errno.h>
      21             : #include <pthread.h>
      22             : #include <setjmp.h>
      23             : #include <stdlib.h>
      24             : #include <string.h>
      25             : #include <limits.h>
      26             : 
      27             : #include <talloc.h>
      28             : #include <tevent.h>
      29             : #include <pthreadpool_tevent.h>
      30             : 
      31             : #include <cmocka.h>
      32             : #include <poll.h>
      33             : 
      34             : struct pthreadpool_tevent_test {
      35             :         struct tevent_context *ev;
      36             :         struct pthreadpool_tevent *upool;
      37             :         struct pthreadpool_tevent *spool;
      38             :         struct pthreadpool_tevent *opool;
      39             : };
      40             : 
      41           1 : static int setup_pthreadpool_tevent(void **state)
      42             : {
      43           1 :         struct pthreadpool_tevent_test *t;
      44           1 :         int ret;
      45           1 :         size_t max_threads;
      46             : 
      47           1 :         t = talloc_zero(NULL, struct pthreadpool_tevent_test);
      48           1 :         assert_non_null(t);
      49             : 
      50           1 :         t->ev = tevent_context_init(t);
      51           1 :         assert_non_null(t->ev);
      52             : 
      53           1 :         ret = pthreadpool_tevent_init(t->ev, UINT_MAX, &t->upool);
      54           1 :         assert_int_equal(ret, 0);
      55             : 
      56           1 :         max_threads = pthreadpool_tevent_max_threads(t->upool);
      57           1 :         assert_int_equal(max_threads, UINT_MAX);
      58             : 
      59           1 :         ret = pthreadpool_tevent_init(t->ev, 1, &t->opool);
      60           1 :         assert_int_equal(ret, 0);
      61             : 
      62           1 :         max_threads = pthreadpool_tevent_max_threads(t->opool);
      63           1 :         assert_int_equal(max_threads, 1);
      64             : 
      65           1 :         ret = pthreadpool_tevent_init(t->ev, 0, &t->spool);
      66           1 :         assert_int_equal(ret, 0);
      67             : 
      68           1 :         max_threads = pthreadpool_tevent_max_threads(t->spool);
      69           1 :         assert_int_equal(max_threads, 0);
      70             : 
      71           1 :         *state = t;
      72             : 
      73           1 :         return 0;
      74             : }
      75             : 
      76           1 : static int teardown_pthreadpool_tevent(void **state)
      77             : {
      78           1 :         struct pthreadpool_tevent_test *t = *state;
      79             : 
      80           1 :         TALLOC_FREE(t);
      81             : 
      82           1 :         return 0;
      83             : }
      84             : 
      85             : int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
      86             :                           void *(*start_routine) (void *), void *arg);
      87             : int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
      88             :                           void *(*start_routine) (void *),  void *arg);
      89             : 
      90           4 : int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
      91             :                           void *(*start_routine) (void *), void *arg)
      92             : {
      93           4 :         int error;
      94             : 
      95           4 :         error = mock_type(int);
      96           4 :         if (error != 0) {
      97             :                 return error;
      98             :         }
      99             : 
     100           2 :         return __real_pthread_create(thread, attr, start_routine, arg);
     101             : }
     102             : 
     103           5 : static void test_job_threadid(void *ptr)
     104             : {
     105           5 :         pthread_t *threadid = ptr;
     106             : 
     107           5 :         *threadid = pthread_self();
     108           5 : }
     109             : 
     110           7 : static int test_create_do(struct tevent_context *ev,
     111             :                           struct pthreadpool_tevent *pool,
     112             :                           bool *executed,
     113             :                           bool *in_main_thread)
     114             : {
     115           7 :         struct tevent_req *req;
     116           7 :         pthread_t zero_thread;
     117           7 :         pthread_t main_thread;
     118           7 :         pthread_t worker_thread;
     119           7 :         bool ok;
     120           7 :         int ret;
     121             : 
     122           7 :         *executed = false;
     123           7 :         *in_main_thread = false;
     124             : 
     125           7 :         memset(&zero_thread, 0, sizeof(zero_thread));
     126           7 :         main_thread = pthread_self();
     127           7 :         worker_thread = zero_thread;
     128             : 
     129           7 :         req = pthreadpool_tevent_job_send(
     130             :                 ev, ev, pool, test_job_threadid, &worker_thread);
     131           7 :         if (req == NULL) {
     132           0 :                 fprintf(stderr, "pthreadpool_tevent_job_send failed\n");
     133           0 :                 return ENOMEM;
     134             :         }
     135             : 
     136           7 :         ok = tevent_req_poll(req, ev);
     137           7 :         if (!ok) {
     138           0 :                 ret = errno;
     139           0 :                 fprintf(stderr, "tevent_req_poll failed: %s\n",
     140             :                         strerror(ret));
     141           0 :                 *executed = !pthread_equal(worker_thread, zero_thread);
     142           0 :                 *in_main_thread = pthread_equal(worker_thread, main_thread);
     143           0 :                 return ret;
     144             :         }
     145             : 
     146             : 
     147           7 :         ret = pthreadpool_tevent_job_recv(req);
     148           7 :         TALLOC_FREE(req);
     149           7 :         *executed = !pthread_equal(worker_thread, zero_thread);
     150           7 :         *in_main_thread = pthread_equal(worker_thread, main_thread);
     151           7 :         if (ret != 0) {
     152           2 :                 fprintf(stderr, "tevent_req_recv failed: %s\n",
     153             :                         strerror(ret));
     154           2 :                 return ret;
     155             :         }
     156             : 
     157             :         return 0;
     158             : }
     159             : 
     160           1 : static void test_create(void **state)
     161             : {
     162           1 :         struct pthreadpool_tevent_test *t = *state;
     163           1 :         bool executed;
     164           1 :         bool in_main_thread;
     165           1 :         int ret;
     166             : 
     167             :         /*
     168             :          * When pthreadpool cannot create the first worker thread,
     169             :          * this job will run in the sync fallback in the main thread.
     170             :          */
     171           1 :         will_return(__wrap_pthread_create, EAGAIN);
     172           1 :         ret = test_create_do(t->ev, t->upool, &executed, &in_main_thread);
     173           1 :         assert_int_equal(ret, EAGAIN);
     174           1 :         assert_false(executed);
     175           1 :         assert_false(in_main_thread);
     176             : 
     177             :         /*
     178             :          * The sync pool won't trigger pthread_create()
     179             :          * It will be triggered by the one pool.
     180             :          */
     181           1 :         will_return(__wrap_pthread_create, EAGAIN);
     182             : 
     183           1 :         ret = test_create_do(t->ev, t->spool, &executed, &in_main_thread);
     184           1 :         assert_int_equal(ret, 0);
     185           1 :         assert_true(executed);
     186           1 :         assert_true(in_main_thread);
     187             : 
     188           1 :         ret = test_create_do(t->ev, t->opool, &executed, &in_main_thread);
     189           1 :         assert_int_equal(ret, EAGAIN);
     190           1 :         assert_false(executed);
     191           1 :         assert_false(in_main_thread);
     192             : 
     193             :         /*
     194             :          * When a thread can be created, the job will run in the worker thread.
     195             :          */
     196           1 :         will_return(__wrap_pthread_create, 0);
     197           1 :         ret = test_create_do(t->ev, t->upool, &executed, &in_main_thread);
     198           1 :         assert_int_equal(ret, 0);
     199           1 :         assert_true(executed);
     200           1 :         assert_false(in_main_thread);
     201             : 
     202           1 :         poll(NULL, 0, 10);
     203             : 
     204             :         /*
     205             :          * Workerthread will still be active for a second; immediately
     206             :          * running another job will also use the worker thread, even
     207             :          * if a new thread cannot be created.
     208             :          */
     209           1 :         ret = test_create_do(t->ev, t->upool, &executed, &in_main_thread);
     210           1 :         assert_int_equal(ret, 0);
     211           1 :         assert_true(executed);
     212           1 :         assert_false(in_main_thread);
     213             : 
     214             :         /*
     215             :          * When a thread can be created, the job will run in the worker thread.
     216             :          */
     217           1 :         will_return(__wrap_pthread_create, 0);
     218           1 :         ret = test_create_do(t->ev, t->opool, &executed, &in_main_thread);
     219           1 :         assert_int_equal(ret, 0);
     220           1 :         assert_true(executed);
     221           1 :         assert_false(in_main_thread);
     222             : 
     223           1 :         poll(NULL, 0, 10);
     224             : 
     225             :         /*
     226             :          * Workerthread will still be active for a second; immediately
     227             :          * running another job will also use the worker thread, even
     228             :          * if a new thread cannot be created.
     229             :          */
     230           1 :         ret = test_create_do(t->ev, t->opool, &executed, &in_main_thread);
     231           1 :         assert_int_equal(ret, 0);
     232           1 :         assert_true(executed);
     233           1 :         assert_false(in_main_thread);
     234           1 : }
     235             : 
     236           1 : int main(int argc, char **argv)
     237             : {
     238           1 :         const struct CMUnitTest tests[] = {
     239             :                 cmocka_unit_test_setup_teardown(test_create,
     240             :                                                 setup_pthreadpool_tevent,
     241             :                                                 teardown_pthreadpool_tevent),
     242             :         };
     243             : 
     244           1 :         cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
     245             : 
     246           1 :         return cmocka_run_group_tests(tests, NULL, NULL);
     247             : }

Generated by: LCOV version 1.14