From 9aa827dcfacded7208501fcfd4c93af7f6cdf07f Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Wed, 25 Nov 2020 11:50:06 -0800 Subject: [PATCH 1/8] debugging on OSX semaphor --- src/common/debug.c | 5 +++-- src/tal/posix/twposix.h | 4 ++-- test/task/custom_dep.c | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/common/debug.c b/src/common/debug.c index b5edc2c..01ce1e7 100644 --- a/src/common/debug.c +++ b/src/common/debug.c @@ -17,6 +17,7 @@ #define _GNU_SOURCE #include #include +#include #endif #include "debug.h" @@ -24,7 +25,7 @@ #ifdef _WIN32 int TWI_Get_tid (void) { return (int)GetThreadId (); } #else -int TWI_Get_tid (void) { return (int)gettid (); } +int TWI_Get_tid (void) { return (int)pthread_self(); } #endif -int TWI_Debug_level = 0; \ No newline at end of file +int TWI_Debug_level = 0; diff --git a/src/tal/posix/twposix.h b/src/tal/posix/twposix.h index 2f4feb6..1c9bf80 100644 --- a/src/tal/posix/twposix.h +++ b/src/tal/posix/twposix.h @@ -12,7 +12,7 @@ #pragma once -#include +#include #include #include @@ -62,4 +62,4 @@ terr_t TWPOSIX_Join (TW_handle_t ht, void **ret); terr_t TWPOSIX_Cancel (TW_handle_t ht); void TWPOSIX_Exit (void *ret); -terr_t TWPOSIX_Err_to_tw_err (int TWI_UNUSED perr); \ No newline at end of file +terr_t TWPOSIX_Err_to_tw_err (int TWI_UNUSED perr); diff --git a/test/task/custom_dep.c b/test/task/custom_dep.c index 1ef3891..40c4e43 100644 --- a/test/task/custom_dep.c +++ b/test/task/custom_dep.c @@ -57,7 +57,7 @@ int Custom_dep_status_change (TW_Task_handle_t task, // Try CAS in my own handle if available if (h == TW_HANDLE_NULL || (parent == h && new_status == TW_TASK_STAT_COMPLETED)) { - if (atomic_compare_exchange_strong (hp, &h, task)) { return TW_TASK_STAT_READY; } + if (atomic_compare_exchange_strong ((_Atomic*)hp, &h, task)) { return TW_TASK_STAT_READY; } } return TW_TASK_STAT_DEPHOLD; @@ -149,4 +149,4 @@ int main (int argc, char *argv[]) { PRINT_TEST_RESULT return nerr; -} \ No newline at end of file +} From 8893b0beb00a23e89094bf1f79807993b2e81f95 Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Wed, 25 Nov 2020 12:19:11 -0800 Subject: [PATCH 2/8] test osx change on linux --- src/tal/posix/twposix_sem.c | 24 +++++++++++++++++------- test/common/semaphore.c | 14 ++++++++++---- test/task/custom_dep.c | 7 ++++++- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/tal/posix/twposix_sem.c b/src/tal/posix/twposix_sem.c index 5728252..393388d 100644 --- a/src/tal/posix/twposix_sem.c +++ b/src/tal/posix/twposix_sem.c @@ -13,15 +13,17 @@ #include "twposix_sem.h" #include - +sem_t *make_semaphore(int value); terr_t TWPOSIX_Sem_create (TW_Handle_t *sem) { terr_t err = TW_SUCCESS; int perr; sem_t *sp = NULL; sp = (sem_t *)TWI_Malloc (sizeof (sem_t)); - - perr = sem_init (sp, 0, 0); + sp = make_semaphore(0); + if(sp == SEM_FAILED) + err = TW_ERR_OS; + //perr = sem_init (sp, 0, 0); CHECK_PERR *sem = sp; @@ -41,11 +43,19 @@ void TWPOSIX_Sem_trydec (TW_Handle_t sem, TWI_Bool_t *success) { } } -void TWPOSIX_Sem_dec (TW_Handle_t sem) { sem_wait (sem); } +void TWPOSIX_Sem_dec (TW_Handle_t sem) { sem_trywait (sem); } void TWPOSIX_Sem_inc (TW_Handle_t sem) { sem_post (sem); } void TWPOSIX_Sem_free (TW_Handle_t sem) { - sem_destroy (sem); - TWI_Free (sem); -} \ No newline at end of file + sem_close(sem); + //sem_destroy (sem); + //TWI_Free (sem); +} + +sem_t *make_semaphore(int value){ + sem_t *semaphore = (sem_t *) malloc(sizeof(sem_t)); + semaphore = sem_open("/semaphore", O_CREAT, 0644, value); + sem_unlink("/semaphore"); + return semaphore; +} diff --git a/test/common/semaphore.c b/test/common/semaphore.c index 69a5ecb..1bd0c9d 100644 --- a/test/common/semaphore.c +++ b/test/common/semaphore.c @@ -6,7 +6,13 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem) { s = (TWT_Semaphore)malloc (sizeof (sem_t)); - ret = sem_init (s, 0, 0); + //ret = sem_init (s, 0, 0); + sem_t *semaphore = (sem_t *) malloc(sizeof(sem_t)); + s = sem_open("/semaphore", O_CREAT, 0644, 0); + sem_unlink("/semaphore"); + if(s == SEM_FAILED) + ret = -1; + if (ret != 0) return TW_ERR_OS; *sem = s; @@ -17,7 +23,7 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem) { terr_t TWT_Sem_dec (TWT_Semaphore sem) { int ret; - ret = sem_wait (sem); + ret = sem_trywait (sem); if (ret != 0) return TW_ERR_OS; return TW_SUCCESS; @@ -35,10 +41,10 @@ terr_t TWT_Sem_inc (TWT_Semaphore sem) { terr_t TWT_Sem_free (TWT_Semaphore sem) { int ret; - ret = sem_destroy (sem); + ret = sem_close(sem); //sem_destroy (sem); if (ret != 0) return TW_ERR_OS; free (sem); return TW_SUCCESS; -} \ No newline at end of file +} diff --git a/test/task/custom_dep.c b/test/task/custom_dep.c index 40c4e43..d00f1db 100644 --- a/test/task/custom_dep.c +++ b/test/task/custom_dep.c @@ -57,7 +57,12 @@ int Custom_dep_status_change (TW_Task_handle_t task, // Try CAS in my own handle if available if (h == TW_HANDLE_NULL || (parent == h && new_status == TW_TASK_STAT_COMPLETED)) { - if (atomic_compare_exchange_strong ((_Atomic*)hp, &h, task)) { return TW_TASK_STAT_READY; } +#ifdef __APPLE__ + if (atomic_compare_exchange_strong ((_Atomic*)hp, &h, task)) { return TW_TASK_STAT_READY; } +#else + if (atomic_compare_exchange_strong (hp, &h, task)) { return TW_TASK_STAT_READY; } +#endif + } return TW_TASK_STAT_DEPHOLD; From da068d7900e9a2e5dd52c101a902ebf097847d4d Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Wed, 25 Nov 2020 14:27:07 -0800 Subject: [PATCH 3/8] Made changes for Linux runing --- src/tal/posix/twposix_sem.c | 10 +++++----- test/common/semaphore.c | 4 ++-- test/common/twtest.h | 3 ++- test/event/file.c | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/tal/posix/twposix_sem.c b/src/tal/posix/twposix_sem.c index 393388d..522cbbc 100644 --- a/src/tal/posix/twposix_sem.c +++ b/src/tal/posix/twposix_sem.c @@ -9,20 +9,20 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Pthread driver implementation */ - +#include #include "twposix_sem.h" #include sem_t *make_semaphore(int value); terr_t TWPOSIX_Sem_create (TW_Handle_t *sem) { terr_t err = TW_SUCCESS; - int perr; + int perr = TW_SUCCESS; sem_t *sp = NULL; sp = (sem_t *)TWI_Malloc (sizeof (sem_t)); sp = make_semaphore(0); if(sp == SEM_FAILED) - err = TW_ERR_OS; + perr = TW_ERR_OS; //perr = sem_init (sp, 0, 0); CHECK_PERR @@ -35,7 +35,7 @@ err_out:; void TWPOSIX_Sem_trydec (TW_Handle_t sem, TWI_Bool_t *success) { int perr; - perr = sem_trywait (sem); + perr = sem_wait (sem); if (perr == 0) { *success = TWI_TRUE; } else { @@ -43,7 +43,7 @@ void TWPOSIX_Sem_trydec (TW_Handle_t sem, TWI_Bool_t *success) { } } -void TWPOSIX_Sem_dec (TW_Handle_t sem) { sem_trywait (sem); } +void TWPOSIX_Sem_dec (TW_Handle_t sem) { sem_wait (sem); } void TWPOSIX_Sem_inc (TW_Handle_t sem) { sem_post (sem); } diff --git a/test/common/semaphore.c b/test/common/semaphore.c index 1bd0c9d..ed6c55c 100644 --- a/test/common/semaphore.c +++ b/test/common/semaphore.c @@ -23,7 +23,7 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem) { terr_t TWT_Sem_dec (TWT_Semaphore sem) { int ret; - ret = sem_trywait (sem); + ret = sem_wait (sem); if (ret != 0) return TW_ERR_OS; return TW_SUCCESS; @@ -44,7 +44,7 @@ terr_t TWT_Sem_free (TWT_Semaphore sem) { ret = sem_close(sem); //sem_destroy (sem); if (ret != 0) return TW_ERR_OS; - free (sem); + //free (sem); return TW_SUCCESS; } diff --git a/test/common/twtest.h b/test/common/twtest.h index 22c9a25..a481895 100644 --- a/test/common/twtest.h +++ b/test/common/twtest.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -66,4 +67,4 @@ typedef sem_t *TWT_Semaphore; terr_t TWT_Sem_create (TWT_Semaphore *sem); terr_t TWT_Sem_dec (TWT_Semaphore sem); terr_t TWT_Sem_inc (TWT_Semaphore sem); -terr_t TWT_Sem_free (TWT_Semaphore sem); \ No newline at end of file +terr_t TWT_Sem_free (TWT_Semaphore sem); diff --git a/test/event/file.c b/test/event/file.c index 8a11ba9..4ce2073 100644 --- a/test/event/file.c +++ b/test/event/file.c @@ -143,7 +143,7 @@ int main (int argc, char **argv) { atomic_int evtnerr = 0; TW_Fd_t fd; char msg[] = "test_msg"; - char cmd[256]; + char cmd[256] = {0}; TW_Event_args_t arg; TW_Event_handle_t evt; TW_Engine_handle_t eng; @@ -181,7 +181,7 @@ int main (int argc, char **argv) { // echo will add \n after the end of the message sprintf (cmd, "echo \"%s\" >> file.txt", msg); - system (cmd); + int r = system (cmd); err = TWT_Sem_dec (sem); CHECK_ERR From f8f6592972ed3c68fd117fe435ddeec5b852c2e3 Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Mon, 30 Nov 2020 11:11:29 -0800 Subject: [PATCH 4/8] Debuging and verifying on Linux --- src/tal/posix/twposix_sem.c | 2 +- test/common/semaphore.c | 3 ++- test/common/twtest.h | 2 +- test/task/task_status.c | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tal/posix/twposix_sem.c b/src/tal/posix/twposix_sem.c index 522cbbc..6a747c9 100644 --- a/src/tal/posix/twposix_sem.c +++ b/src/tal/posix/twposix_sem.c @@ -20,7 +20,7 @@ terr_t TWPOSIX_Sem_create (TW_Handle_t *sem) { sem_t *sp = NULL; sp = (sem_t *)TWI_Malloc (sizeof (sem_t)); - sp = make_semaphore(0); + sp = make_semaphore(1); if(sp == SEM_FAILED) perr = TW_ERR_OS; //perr = sem_init (sp, 0, 0); diff --git a/test/common/semaphore.c b/test/common/semaphore.c index ed6c55c..da9dadb 100644 --- a/test/common/semaphore.c +++ b/test/common/semaphore.c @@ -8,7 +8,7 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem) { //ret = sem_init (s, 0, 0); sem_t *semaphore = (sem_t *) malloc(sizeof(sem_t)); - s = sem_open("/semaphore", O_CREAT, 0644, 0); + s = sem_open("/semaphore", O_CREAT, 0644, 1); sem_unlink("/semaphore"); if(s == SEM_FAILED) ret = -1; @@ -24,6 +24,7 @@ terr_t TWT_Sem_dec (TWT_Semaphore sem) { int ret; ret = sem_wait (sem); + printf("%s:%d: ret = %d, Errno str = %s\n", __func__, __LINE__, ret, strerror(errno)); if (ret != 0) return TW_ERR_OS; return TW_SUCCESS; diff --git a/test/common/twtest.h b/test/common/twtest.h index a481895..e5408b6 100644 --- a/test/common/twtest.h +++ b/test/common/twtest.h @@ -2,7 +2,7 @@ #include #include #include - +#include #ifdef _WIN32 #else #endif diff --git a/test/task/task_status.c b/test/task/task_status.c index 1695da9..3e57c2d 100644 --- a/test/task/task_status.c +++ b/test/task/task_status.c @@ -100,4 +100,4 @@ int main (int argc, char *argv[]) { PRINT_TEST_RESULT return nerr; -} \ No newline at end of file +} From 4609bd1c6cc71080f704f6239a14ee258448b380 Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Mon, 7 Dec 2020 16:25:49 -0800 Subject: [PATCH 5/8] Clean up code and prepare for stress test --- src/tal/posix/twposix_sem.c | 19 +++++++++---------- test/common/semaphore.c | 23 +++++++++++------------ test/common/twtest.h | 6 ++++++ test/event/Makefile.am | 5 ++--- test/event/file.c | 1 + test/task/Makefile.am | 4 ++-- test/task/custom_dep.c | 2 +- 7 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/tal/posix/twposix_sem.c b/src/tal/posix/twposix_sem.c index 6a747c9..bf79e42 100644 --- a/src/tal/posix/twposix_sem.c +++ b/src/tal/posix/twposix_sem.c @@ -20,10 +20,9 @@ terr_t TWPOSIX_Sem_create (TW_Handle_t *sem) { sem_t *sp = NULL; sp = (sem_t *)TWI_Malloc (sizeof (sem_t)); - sp = make_semaphore(1); + sp = make_semaphore(0); if(sp == SEM_FAILED) perr = TW_ERR_OS; - //perr = sem_init (sp, 0, 0); CHECK_PERR *sem = sp; @@ -47,15 +46,15 @@ void TWPOSIX_Sem_dec (TW_Handle_t sem) { sem_wait (sem); } void TWPOSIX_Sem_inc (TW_Handle_t sem) { sem_post (sem); } -void TWPOSIX_Sem_free (TW_Handle_t sem) { - sem_close(sem); - //sem_destroy (sem); - //TWI_Free (sem); -} +void TWPOSIX_Sem_free (TW_Handle_t sem) { sem_close(sem); } sem_t *make_semaphore(int value){ sem_t *semaphore = (sem_t *) malloc(sizeof(sem_t)); - semaphore = sem_open("/semaphore", O_CREAT, 0644, value); - sem_unlink("/semaphore"); - return semaphore; + + char sem_name[128] = {}; + sprintf(sem_name, "sem-%d", pthread_self()); + semaphore = sem_open(sem_name, O_CREAT, 0644, 0); + sem_unlink(sem_name); + + return semaphore; } diff --git a/test/common/semaphore.c b/test/common/semaphore.c index da9dadb..818bc13 100644 --- a/test/common/semaphore.c +++ b/test/common/semaphore.c @@ -3,15 +3,16 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem) { int ret; TWT_Semaphore s; - s = (TWT_Semaphore)malloc (sizeof (sem_t)); - //ret = sem_init (s, 0, 0); - sem_t *semaphore = (sem_t *) malloc(sizeof(sem_t)); - s = sem_open("/semaphore", O_CREAT, 0644, 1); - sem_unlink("/semaphore"); - if(s == SEM_FAILED) - ret = -1; + char sem_name[128] = {}; + sprintf(sem_name, "sem-%d", pthread_self()); + + s = sem_open(sem_name, O_CREAT, 0644, 0); + sem_unlink(sem_name); + + if(s == SEM_FAILED) ret = -1; + else ret = 0; if (ret != 0) return TW_ERR_OS; @@ -41,11 +42,9 @@ terr_t TWT_Sem_inc (TWT_Semaphore sem) { terr_t TWT_Sem_free (TWT_Semaphore sem) { int ret; - - ret = sem_close(sem); //sem_destroy (sem); - if (ret != 0) return TW_ERR_OS; - - //free (sem); + ret = sem_close(sem); + if (ret != 0) + return TW_ERR_OS; return TW_SUCCESS; } diff --git a/test/common/twtest.h b/test/common/twtest.h index e5408b6..745ff0d 100644 --- a/test/common/twtest.h +++ b/test/common/twtest.h @@ -3,6 +3,7 @@ #include #include #include +#include #ifdef _WIN32 #else #endif @@ -68,3 +69,8 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem); terr_t TWT_Sem_dec (TWT_Semaphore sem); terr_t TWT_Sem_inc (TWT_Semaphore sem); terr_t TWT_Sem_free (TWT_Semaphore sem); +void _semaphore_bug_warning(){ +#ifdef __APPLE__ + assert(0 && "Taskworks has a known bug with semaphore on OSX that may lead to a deadlock, not fixed yet."); +#endif +} diff --git a/test/event/Makefile.am b/test/event/Makefile.am index 965dffc..7266bb8 100644 --- a/test/event/Makefile.am +++ b/test/event/Makefile.am @@ -18,11 +18,10 @@ LDADD = $(top_builddir)/src/libtw.la \ TESTPROGRAMS = init \ engine \ - file \ timer \ socket \ - poll - + poll \ + file if HAVE_MPI TESTPROGRAMS += mpi \ mpi_req diff --git a/test/event/file.c b/test/event/file.c index 4ce2073..8092d39 100644 --- a/test/event/file.c +++ b/test/event/file.c @@ -147,6 +147,7 @@ int main (int argc, char **argv) { TW_Event_args_t arg; TW_Event_handle_t evt; TW_Engine_handle_t eng; + _semaphore_bug_warning(); PRINT_TEST_MSG ("Check if file event triggers correctly"); diff --git a/test/task/Makefile.am b/test/task/Makefile.am index d7f3bb4..6431a1d 100644 --- a/test/task/Makefile.am +++ b/test/task/Makefile.am @@ -22,8 +22,8 @@ TESTPROGRAMS = init \ task \ task_dep \ task_barrier \ - custom_dep \ - task_status + task_status \ + custom_dep # mutual_dep TEST_DRIVERS= "NATIVE" diff --git a/test/task/custom_dep.c b/test/task/custom_dep.c index d00f1db..6362ac7 100644 --- a/test/task/custom_dep.c +++ b/test/task/custom_dep.c @@ -80,7 +80,7 @@ int main (int argc, char *argv[]) { TW_Task_dep_handler_t dep; TW_Task_handle_t *task = NULL; task_data *datas = NULL; - + _semaphore_bug_warning(); PRINT_TEST_MSG ("Check if Taskworks correctly handles customized dependency"); if (argc > 1) { nworker = atoi (argv[1]); } From fa2dce82b481ddb43f91416c861754c55ea796ba Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Mon, 7 Dec 2020 16:53:25 -0800 Subject: [PATCH 6/8] fixed warning info --- test/common/semaphore.c | 6 ++++++ test/common/twtest.h | 6 +----- test/event/file.c | 3 ++- test/task/custom_dep.c | 4 +++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/test/common/semaphore.c b/test/common/semaphore.c index 818bc13..7666c2b 100644 --- a/test/common/semaphore.c +++ b/test/common/semaphore.c @@ -48,3 +48,9 @@ terr_t TWT_Sem_free (TWT_Semaphore sem) { return TW_SUCCESS; } + +void SEMAPHORE_BUG_WARNING(){ +#ifdef __APPLE__ + assert(0 && "Taskworks has a known bug with semaphore on OSX that may lead to a deadlock, not fixed yet."); +#endif +} diff --git a/test/common/twtest.h b/test/common/twtest.h index 745ff0d..477f97b 100644 --- a/test/common/twtest.h +++ b/test/common/twtest.h @@ -69,8 +69,4 @@ terr_t TWT_Sem_create (TWT_Semaphore *sem); terr_t TWT_Sem_dec (TWT_Semaphore sem); terr_t TWT_Sem_inc (TWT_Semaphore sem); terr_t TWT_Sem_free (TWT_Semaphore sem); -void _semaphore_bug_warning(){ -#ifdef __APPLE__ - assert(0 && "Taskworks has a known bug with semaphore on OSX that may lead to a deadlock, not fixed yet."); -#endif -} +void SEMAPHORE_BUG_WARNING(); diff --git a/test/event/file.c b/test/event/file.c index 8092d39..f2eb919 100644 --- a/test/event/file.c +++ b/test/event/file.c @@ -147,7 +147,8 @@ int main (int argc, char **argv) { TW_Event_args_t arg; TW_Event_handle_t evt; TW_Engine_handle_t eng; - _semaphore_bug_warning(); + + SEMAPHORE_BUG_WARNING(); PRINT_TEST_MSG ("Check if file event triggers correctly"); diff --git a/test/task/custom_dep.c b/test/task/custom_dep.c index 6362ac7..c4eb638 100644 --- a/test/task/custom_dep.c +++ b/test/task/custom_dep.c @@ -80,7 +80,9 @@ int main (int argc, char *argv[]) { TW_Task_dep_handler_t dep; TW_Task_handle_t *task = NULL; task_data *datas = NULL; - _semaphore_bug_warning(); + + SEMAPHORE_BUG_WARNING(); + PRINT_TEST_MSG ("Check if Taskworks correctly handles customized dependency"); if (argc > 1) { nworker = atoi (argv[1]); } From cb3b7b8d11580480d09adbacff30b5a772e6aa9b Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Mon, 7 Dec 2020 17:09:23 -0800 Subject: [PATCH 7/8] minor bug fix --- test/common/semaphore.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/common/semaphore.c b/test/common/semaphore.c index 7666c2b..940d87d 100644 --- a/test/common/semaphore.c +++ b/test/common/semaphore.c @@ -25,7 +25,6 @@ terr_t TWT_Sem_dec (TWT_Semaphore sem) { int ret; ret = sem_wait (sem); - printf("%s:%d: ret = %d, Errno str = %s\n", __func__, __LINE__, ret, strerror(errno)); if (ret != 0) return TW_ERR_OS; return TW_SUCCESS; From 53d6ecb08088885bee92b99f9af6e43cbd6a63eb Mon Sep 17 00:00:00 2001 From: Tonglin Li Date: Tue, 15 Dec 2020 11:51:02 -0800 Subject: [PATCH 8/8] minor reformating to resolve a conflict --- src/common/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/debug.c b/src/common/debug.c index 01ce1e7..421f3d1 100644 --- a/src/common/debug.c +++ b/src/common/debug.c @@ -25,7 +25,7 @@ #ifdef _WIN32 int TWI_Get_tid (void) { return (int)GetThreadId (); } #else -int TWI_Get_tid (void) { return (int)pthread_self(); } +int TWI_Get_tid (void) { return (int)pthread_self (); } #endif int TWI_Debug_level = 0;