#include "spdk_cunit.h"
+#include "spdk_internal/thread.h"
+
#include "thread/thread.c"
-#include "common/lib/test_env.c"
#include "common/lib/ut_multithread.c"
-static void
-_send_msg(spdk_thread_fn fn, void *ctx, void *thread_ctx)
+static int g_sched_rc = 0;
+
+static int
+_thread_schedule(struct spdk_thread *thread)
{
- fn(ctx);
+ return g_sched_rc;
}
static void
thread_alloc(void)
{
- CU_ASSERT(TAILQ_EMPTY(&g_threads));
- allocate_threads(1);
- CU_ASSERT(!TAILQ_EMPTY(&g_threads));
- free_threads();
- CU_ASSERT(TAILQ_EMPTY(&g_threads));
+ struct spdk_thread *thread;
+
+ /* No schedule callback */
+ spdk_thread_lib_init(NULL, 0);
+ thread = spdk_thread_create(NULL, NULL);
+ SPDK_CU_ASSERT_FATAL(thread != NULL);
+ spdk_set_thread(thread);
+ spdk_thread_exit(thread);
+ spdk_thread_destroy(thread);
+ spdk_thread_lib_fini();
+
+ /* Schedule callback exists */
+ spdk_thread_lib_init(_thread_schedule, 0);
+
+ /* Scheduling succeeds */
+ g_sched_rc = 0;
+ thread = spdk_thread_create(NULL, NULL);
+ SPDK_CU_ASSERT_FATAL(thread != NULL);
+ spdk_set_thread(thread);
+ spdk_thread_exit(thread);
+ spdk_thread_destroy(thread);
+
+ /* Scheduling fails */
+ g_sched_rc = -1;
+ thread = spdk_thread_create(NULL, NULL);
+ SPDK_CU_ASSERT_FATAL(thread == NULL);
+
+ spdk_thread_lib_fini();
}
static void
allocate_threads(1);
set_thread(0);
- reset_time();
+ MOCK_SET(spdk_get_ticks, 0);
/* Register a poller with no-wait time and test execution */
poller = spdk_poller_register(poller_run_done, &poller_run, 0);
CU_ASSERT(poller != NULL);
poll_threads();
CU_ASSERT(poller_run == false);
- increment_time(1000);
+ spdk_delay_us(1000);
poll_threads();
CU_ASSERT(poller_run == true);
- reset_time();
poller_run = false;
poll_threads();
CU_ASSERT(poller_run == false);
- increment_time(1000);
+ spdk_delay_us(1000);
poll_threads();
CU_ASSERT(poller_run == true);
int count = 0;
allocate_threads(3);
- spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int), NULL);
set_thread(0);
+ spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int), NULL);
ch0 = spdk_get_io_channel(&io_target);
set_thread(1);
ch1 = spdk_get_io_channel(&io_target);
*/
set_thread(0);
spdk_put_io_channel(ch0);
+ poll_threads();
spdk_for_each_channel(&io_target, channel_msg, &count, channel_cpl);
poll_threads();
int io_target;
allocate_threads(1);
+ set_thread(0);
CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
spdk_io_device_register(&io_target, channel_create, channel_destroy, sizeof(int), NULL);
CU_ASSERT(!TAILQ_EMPTY(&g_io_devices));
dev = TAILQ_FIRST(&g_io_devices);
SPDK_CU_ASSERT_FATAL(dev != NULL);
CU_ASSERT(TAILQ_NEXT(dev, tailq) == NULL);
- set_thread(0);
ch0 = spdk_get_io_channel(&io_target);
spdk_for_each_channel(&io_target, unreg_ch_done, &ctx, unreg_foreach_done);
struct spdk_thread *thread;
const char *name;
+ spdk_thread_lib_init(NULL, 0);
+
/* Create thread with no name, which automatically generates one */
- spdk_allocate_thread(_send_msg, NULL, NULL, NULL, NULL);
+ thread = spdk_thread_create(NULL, NULL);
+ spdk_set_thread(thread);
thread = spdk_get_thread();
SPDK_CU_ASSERT_FATAL(thread != NULL);
name = spdk_thread_get_name(thread);
CU_ASSERT(name != NULL);
- spdk_free_thread();
+ spdk_thread_exit(thread);
+ spdk_thread_destroy(thread);
/* Create thread named "test_thread" */
- spdk_allocate_thread(_send_msg, NULL, NULL, NULL, "test_thread");
+ thread = spdk_thread_create("test_thread", NULL);
+ spdk_set_thread(thread);
thread = spdk_get_thread();
SPDK_CU_ASSERT_FATAL(thread != NULL);
name = spdk_thread_get_name(thread);
SPDK_CU_ASSERT_FATAL(name != NULL);
CU_ASSERT(strcmp(name, "test_thread") == 0);
- spdk_free_thread();
+ spdk_thread_exit(thread);
+ spdk_thread_destroy(thread);
+
+ spdk_thread_lib_fini();
}
static uint64_t device1;
struct spdk_io_channel *ch1, *ch2;
void *ctx;
- spdk_allocate_thread(_send_msg, NULL, NULL, NULL, "thread0");
+ allocate_threads(1);
+ set_thread(0);
+
spdk_io_device_register(&device1, create_cb_1, destroy_cb_1, sizeof(ctx1), NULL);
spdk_io_device_register(&device2, create_cb_2, destroy_cb_2, sizeof(ctx2), NULL);
g_destroy_cb_calls = 0;
spdk_put_io_channel(ch2);
+ poll_threads();
CU_ASSERT(g_destroy_cb_calls == 0);
g_create_cb_calls = 0;
g_destroy_cb_calls = 0;
spdk_put_io_channel(ch1);
+ poll_threads();
CU_ASSERT(g_destroy_cb_calls == 1);
g_destroy_cb_calls = 0;
spdk_put_io_channel(ch2);
+ poll_threads();
CU_ASSERT(g_destroy_cb_calls == 1);
ch1 = spdk_get_io_channel(&device3);
CU_ASSERT(ch1 == NULL);
spdk_io_device_unregister(&device1, NULL);
+ poll_threads();
spdk_io_device_unregister(&device2, NULL);
+ poll_threads();
+ CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
+ free_threads();
+ CU_ASSERT(TAILQ_EMPTY(&g_threads));
+}
+
+static int
+create_cb(void *io_device, void *ctx_buf)
+{
+ uint64_t *refcnt = (uint64_t *)ctx_buf;
+
+ CU_ASSERT(*refcnt == 0);
+ *refcnt = 1;
+
+ return 0;
+}
+
+static void
+destroy_cb(void *io_device, void *ctx_buf)
+{
+ uint64_t *refcnt = (uint64_t *)ctx_buf;
+
+ CU_ASSERT(*refcnt == 1);
+ *refcnt = 0;
+}
+
+/**
+ * This test is checking that a sequence of get, put, get, put without allowing
+ * the deferred put operation to complete doesn't result in releasing the memory
+ * for the channel twice.
+ */
+static void
+channel_destroy_races(void)
+{
+ uint64_t device;
+ struct spdk_io_channel *ch;
+
+ allocate_threads(1);
+ set_thread(0);
+
+ spdk_io_device_register(&device, create_cb, destroy_cb, sizeof(uint64_t), NULL);
+
+ ch = spdk_get_io_channel(&device);
+ SPDK_CU_ASSERT_FATAL(ch != NULL);
+
+ spdk_put_io_channel(ch);
+
+ ch = spdk_get_io_channel(&device);
+ SPDK_CU_ASSERT_FATAL(ch != NULL);
+
+ spdk_put_io_channel(ch);
+ poll_threads();
+
+ spdk_io_device_unregister(&device, NULL);
+ poll_threads();
+
CU_ASSERT(TAILQ_EMPTY(&g_io_devices));
- spdk_free_thread();
+ free_threads();
CU_ASSERT(TAILQ_EMPTY(&g_threads));
}
CU_add_test(suite, "for_each_channel_remove", for_each_channel_remove) == NULL ||
CU_add_test(suite, "for_each_channel_unreg", for_each_channel_unreg) == NULL ||
CU_add_test(suite, "thread_name", thread_name) == NULL ||
- CU_add_test(suite, "channel", channel) == NULL
+ CU_add_test(suite, "channel", channel) == NULL ||
+ CU_add_test(suite, "channel_destroy_races", channel_destroy_races) == NULL
) {
CU_cleanup_registry();
return CU_get_error();