text: /*
   * Copyright (c) 2016 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include "test_sched.h"
  #define THREADS_NUM     3
  #define DURATION	1
  
  BUILD_ASSERT(THREADS_NUM <= MAX_NUM_THREAD);
  
  static struct thread_data tdata[THREADS_NUM];
  static struct k_thread tthread[THREADS_NUM];
  static int old_prio, init_prio;
  
  struct k_thread t;
  
  K_SEM_DEFINE(pend_sema, 0, 1);
  K_SEM_DEFINE(timer_sema, 0, 1);
  struct k_timer timer;
  
  static void thread_entry(void *p1, void *p2, void *p3)
  {
  	int sleep_ms = POINTER_TO_INT(p2);
  
  	if (sleep_ms > 0) {
  		k_sleep(sleep_ms);
  	}
  
  	int tnum = POINTER_TO_INT(p1);
  
  	tdata[tnum].executed = 1;
  }
  
  static void setup_threads(void)
  {
  	old_prio = k_thread_priority_get(k_current_get());
  	for (int i = 0; i < THREADS_NUM; i++) {
  		if (i == 0) {
  			/* spawn thread with higher priority */
  			tdata[i].priority = init_prio - 1;
  		} else if (i == 1) {
  			/* spawn thread with same priority */
  			tdata[i].priority = init_prio;
  		} else {
  			/* spawn thread with lower priority */
  			tdata[i].priority = init_prio + 1;
  		}
  		tdata[i].executed = 0;
  	}
  	k_thread_priority_set(k_current_get(), init_prio);
  }
  
  static void spawn_threads(int sleep_sec)
  {
  	for (int i = 0; i < THREADS_NUM; i++) {
  		tdata[i].tid = k_thread_create(&tthread[i], tstacks[i],
  					       STACK_SIZE, thread_entry,
  					       INT_TO_POINTER(i),
  					       INT_TO_POINTER(sleep_sec),
  					       NULL, tdata[i].priority, 0,
  					       K_NO_WAIT);
  	}
  }
  
  static void teardown_threads(void)
  {
  	for (int i = 0; i < THREADS_NUM; i++) {
  		k_thread_abort(tdata[i].tid);
  	}
  	k_thread_priority_set(k_current_get(), old_prio);
  }
  
  static void timer_handler(struct k_timer *timer)
  {
  	ARG_UNUSED(timer);
  	k_sem_give(&timer_sema);
  }
  
  static void thread_handler(void *p1, void *p2, void *p3)
  {
  	k_timer_init(&timer, timer_handler, NULL);
  	k_timer_start(&timer, DURATION, K_NO_WAIT);
  }
  
  /*test cases*/
  
  /**
   * @brief Validate the behavior of cooperative thread
   * when it yields
   *
   * @ingroup kernel_sched_tests
   *
   * @details Create 3 threads of priority -2, -1 and 0.
   * Yield the main thread which is cooperative. Check
   * if all the threads gets executed.
   */
  void test_yield_cooperative(void)
  {
  
  	/* set current thread to a cooperative priority */
  	init_prio = -1;
  	setup_threads();
  
  	spawn_threads(0);
  	/* checkpoint: only higher priority thread get executed when yield */
  	k_yield();
  	zassert_true(tdata[0].executed == 1, NULL);
  	zassert_true(tdata[1].executed == 1, NULL);
  	for (int i = 2; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  	/* restore environment */
  	teardown_threads();
  }
  /**
   * @brief Validate the behavior of cooperative thread when it sleeps
   *
   * @details Create 3 threads of priority -2, -1 and 0. Put the main
   * thread in timeout queue by calling k_sleep() which is cooperative.
   * Check if all the threads gets executed.
   *
   * @ingroup kernel_sched_tests
   */
  void test_sleep_cooperative(void)
  {
  	/* set current thread to a cooperative priority */
  	init_prio = -1;
  	setup_threads();
  
  	spawn_threads(0);
  	/* checkpoint: all ready threads get executed when k_sleep */
  	k_sleep(K_MSEC(100));
  	for (int i = 0; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 1, NULL);
  	}
  
  	/* restore environment */
  	teardown_threads();
  }
  
  void test_busy_wait_cooperative(void)
  {
  	/* set current thread to a preemptible priority */
  	init_prio = -1;
  	setup_threads();
  
  	spawn_threads(0);
  	k_busy_wait(100000); /* 100 ms */
  	/* checkpoint: No other threads get executed */
  	for (int i = 0; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  	/* restore environment */
  	teardown_threads();
  }
  
  /**
   * @brief Validate k_wakeup()
   *
   * @details Create 3 threads with main thread with priority 0
   * and other threads with -1, 0 ,+1 priority. Now -1 priority
   * thread gets executed and it is made to sleep for 10 sec.
   * Now, wake up the -1 priority thread and check if it starts
   * executing.
   *
   * @see k_wakeup()
   *
   * @ingroup kernel_sched_tests
   */
  void test_sleep_wakeup_preemptible(void)
  {
  	/* set current thread to a preemptible priority */
  	init_prio = 0;
  	setup_threads();
  
  	spawn_threads(10 * 1000); /* 10 second */
  	/* checkpoint: lower threads not executed, high threads are in sleep */
  	for (int i = 0; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  	k_wakeup(tdata[0].tid);
  	zassert_true(tdata[0].executed == 1, NULL);
  	/* restore environment */
  	teardown_threads();
  }
  
  static int executed;
  static void coop_thread(void *p1, void *p2, void *p3)
  {
  	k_sem_take(&pend_sema, K_MSEC(100));
  	executed = 1;
  }
  
  /**
   * @brief Verify k_wakeup() behavior on pending thread
   *
   * @details The test creates a cooperative thread and let
   * it wait for semaphore. Then calls k_wakeup(). The k_wakeup()
   * call should return gracefully without waking up the thread
   *
   * @see k_wakeup()
   *
   * @ingroup kernel_sched_tests
   */
  void test_pending_thread_wakeup(void)
  {
  	/* Make current thread preemptible */
  	k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(1));
  
  	/* Create a thread which waits for semaphore */
  	k_tid_t tid = k_thread_create(&t, tstack, STACK_SIZE,
  				      (k_thread_entry_t)coop_thread,
  				      NULL, NULL, NULL,
  				      K_PRIO_COOP(1), 0, K_NO_WAIT);
  
  	zassert_false(executed == 1, "The thread didn't wait"
  		      " for semaphore acquisition");
  
  	/* Call wakeup on pending thread */
  	k_wakeup(tid);
  
  	/* TESTPOINT: k_wakeup() shouldn't resume
  	 * execution of pending thread
  	 */
  	zassert_true(executed != 1, "k_wakeup woke up a"
  		     " pending thread!");
  
  	k_thread_abort(tid);
  }
  
  /**
   * @brief Validate preemptive thread behavior with time slice
   *
   * @details Create 3 threads with -1, 0, and 1 as priority, setup
   * time slice for threads with priority 0. Make sure the threads
   * with equal priorities are executed in time slice.
   *
   * @ingroup kernel_sched_tests
   */
  void test_time_slicing_preemptible(void)
  {
  #ifdef CONFIG_TIMESLICING
  	/* set current thread to a preemptible priority */
  	init_prio = 0;
  	setup_threads();
  
  	k_sched_time_slice_set(200, 0); /* 200 ms */
  	spawn_threads(0);
  	/* checkpoint: higher priority threads get executed immediately */
  	zassert_true(tdata[0].executed == 1, NULL);
  	k_busy_wait(500000); /* 500 ms */
  	/* checkpoint: equal priority threads get executed every time slice */
  	zassert_true(tdata[1].executed == 1, NULL);
  	for (int i = 2; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  
  	/* restore environment */
  	k_sched_time_slice_set(0, 0); /* disable time slice */
  	teardown_threads();
  #else /* CONFIG_TIMESLICING */
  	ztest_test_skip();
  #endif /* CONFIG_TIMESLICING */
  }
  
  /**
   * @brief Check the behavior of preemptive thread with k_busy_wait()
   *
   * @details Create 3 threads with -1, 0, and 1 as priority,
   * setup time slice for threads with priority 0. Make sure the
   * threads with equal priorities are executed in time slice.
   * Also run k_busy_wait() for 5 secs and check if other threads
   * are not executed at that time.
   *
   * @see k_busy_wait()
   *
   * @ingroup kernel_sched_tests
   */
  void test_time_slicing_disable_preemptible(void)
  {
  #ifdef CONFIG_TIMESLICING
  	/* set current thread to a preemptible priority */
  	init_prio = 0;
  	setup_threads();
  
  	spawn_threads(0);
  	/* checkpoint: higher priority threads get executed immediately */
  	zassert_true(tdata[0].executed == 1, NULL);
  	k_busy_wait(500000); /* 500 ms */
  	/* checkpoint: equal priority threads get executed every time slice */
  	zassert_true(tdata[1].executed == 0, NULL);
  	for (int i = 2; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  	/* restore environment */
  	teardown_threads();
  #else /* CONFIG_TIMESLICING */
  	ztest_test_skip();
  #endif /* CONFIG_TIMESLICING */
  }
  
  /**
   * @brief Lock the scheduler when preemptive threads are running
   *
   * @details Create 3 threads and lock the scheduler. Make sure that the
   * threads are not executed. Call k_sleep() and check if the threads
   * have executed.
   *
   * @ingroup kernel_sched_tests
   */
  void test_lock_preemptible(void)
  {
  	/* set current thread to a preemptible priority */
  	init_prio = 0;
  	setup_threads();
  
  	k_sched_lock();
  	spawn_threads(0);
  	/* do critical thing */
  	k_busy_wait(100000);
  	/* checkpoint: all other threads not been executed */
  	for (int i = 0; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  	/* make current thread unready */
  	k_sleep(K_MSEC(100));
  	/* checkpoint: all other threads get executed */
  	for (int i = 0; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 1, NULL);
  	}
  	/* restore environment */
  	teardown_threads();
  }
  
  /**
   * @brief Validate k_sched_lock() and k_sched_unlock()
   *
   * @details  Lock the scheduler and create 3 threads. Check
   * that the threads are not executed. Call k_sched_unlock()
   * and check if the threads have executed.
   *
   * @see k_sched_lock(), k_sched_unlock()
   *
   * @ingroup kernel_sched_tests
   */
  void test_unlock_preemptible(void)
  {
  	/* set current thread to a preemptible priority */
  	init_prio = 0;
  	setup_threads();
  
  	k_sched_lock();
  	spawn_threads(0);
  	/* do critical thing */
  	k_busy_wait(100000);
  
  	k_sched_unlock();
  
  	/* ensure threads of equal priority can run */
  	k_yield();
  
  	/* checkpoint: higher and equal threads get executed */
  	zassert_true(tdata[0].executed == 1, NULL);
  	zassert_true(tdata[1].executed == 1, NULL);
  	zassert_true(tdata[2].executed == 0, NULL);
  
  	/* restore environment */
  	teardown_threads();
  }
  
  /**
   * @brief Validate nested k_sched_lock() and k_sched_unlock()
   *
   * @details In a preemptive thread, lock the scheduler twice and
   * create a cooperative thread.  Call k_sched_unlock() and check the
   * cooperative thread haven't executed.  Unlock it again to see the
   * thread have executed this time.
   *
   * @see k_sched_lock(), k_sched_unlock()
   *
   * @ingroup kernel_sched_tests
   */
  void test_unlock_nested_sched_lock(void)
  {
  	/* set current thread to a preemptible priority */
  	init_prio = 0;
  	setup_threads();
  
  	/* take the scheduler lock twice */
  	k_sched_lock();
  	k_sched_lock();
  
  	/* spawn threads without wait */
  	spawn_threads(0);
  
  	/* do critical thing */
  	k_busy_wait(100000);
  
  	/* unlock once; this shouldn't let other threads to run */
  	k_sched_unlock();
  
  	/* checkpoint: no threads get executed */
  	for (int i = 0; i < THREADS_NUM; i++) {
  		zassert_true(tdata[i].executed == 0, NULL);
  	}
  
  	/* unlock another; this let the higher thread to run */
  	k_sched_unlock();
  
  	/* Ensure threads of equal priority run */
  	k_yield();
  
  	/* checkpoint: higher threads NOT get executed */
  	zassert_true(tdata[0].executed == 1, NULL);
  	zassert_true(tdata[1].executed == 1, NULL);
  	zassert_true(tdata[2].executed == 0, NULL);
  
  	/* restore environment */
  	teardown_threads();
  }
  
  /**
   * @brief validate k_wakeup() in some corner scenario
   * @details trigger a timer and after expiration of timer
   * call k_wakeup(), even the thread is not in sleep state neither
   * in pending state
   *
   * @see k_wakeup()
   *
   * @ingroup kernel_sched_tests
   */
  void test_wakeup_expired_timer_thread(void)
  {
  	k_tid_t tid = k_thread_create(&tthread[0], tstack, STACK_SIZE,
  					thread_handler, NULL, NULL, NULL,
  					K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
  	k_sem_take(&timer_sema, K_FOREVER);
  	/* wakeup a thread if the timer is expired */
  	k_wakeup(tid);
  	k_thread_abort(tid);
  }
  