/* ------------------------------------------------------------------ * Asynchorous verification related variables and utilities * * -----------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include "async_veri.h" #include "test_commands.h" /* async verification list * Note: * 1) access to this list must be protected by mutex lock */ static struct stm_test_run_node_type *stm_async_veri_node_list = NULL; /* list head for async verification node list */ static struct stm_test_run_node_type *ptr_async_veri_node_last = NULL; /* list tail for async verification node list */ pthread_mutex_t mux_async_list = PTHREAD_MUTEX_INITIALIZER; /* mutex lock, for async node list */ /* async verification thread id */ pthread_t thread_async_id; /* global const */ #ifdef LOCAL_TEST_STUB const char *ETB_FILEPATH = "./debug_stm/ETB"; #else #warning Need to define ETB filepath const char *ETB_FILEPATH = "/sys/kernel/debug/stm/ETB"; #endif /* for async verification thread dependencies */ static bool async_verification_finished = false; pthread_cond_t cond_async_veri = PTHREAD_COND_INITIALIZER; pthread_mutex_t mux_async_veri = PTHREAD_MUTEX_INITIALIZER; static bool all_commands_sent = false; /* set async_verification_finished */ void set_async_verification_finished(bool finished) { async_verification_finished = finished; } /* get async_verification_finished */ bool get_async_verification_finished(void) { return async_verification_finished; } /* to initialize data and structures related to async verification list * Input: none * Return: none */ inline void INIT_ASYNC_VERI_LIST(void) { /* init of mutex */ pthread_mutex_init(&mux_async_list, NULL); return; } /* to append ptr_test_node to async verification list * Input: * - ptr_test_node: test node to be appended * Return: * - 0: success * - -1: failure */ int append_async_verification_node(struct stm_test_run_node_type *ptr_test_node) { /* acquire mutex lock */ pthread_mutex_lock(&mux_async_list); TDBG("ASYNC node append id=0x%08lx.\n", (unsigned long)ptr_test_node); /* close off ptr_test_node */ ptr_test_node->next_async = NULL; /* append to list tail */ /* when not adding the first node */ if (ptr_async_veri_node_last != NULL) ptr_async_veri_node_last->next_async = ptr_test_node; ptr_async_veri_node_last = ptr_test_node; /* adjust list head when first append */ if (stm_async_veri_node_list == NULL) stm_async_veri_node_list = ptr_async_veri_node_last; /* release mutex lock */ pthread_mutex_unlock(&mux_async_list); return 0; } /* to wait for async verification to finish or timeout * Input: none * Return: * - 0: success * - -1: failure */ int wait_for_async_verification(void) { int ret = -1; struct timespec async_veri_timeout; /* set all_commands_sent */ all_commands_sent = true; /* acquire mutex lock */ pthread_mutex_lock(&mux_async_list); /* if async verification list is empty */ if (stm_async_veri_node_list == NULL) { TDBG("Async verification list is empty. No need to wait.\n"); /* release mutex lock */ pthread_mutex_unlock(&mux_async_list); /* cancel async.veri thread */ ret = pthread_cancel(thread_async_id); if (ret != 0) TDBG("Cancellation of thread_async_veri" \ "fication failed. \n"); goto thread_async_join; } /* release mutex lock */ pthread_mutex_unlock(&mux_async_list); /* wait for conditions to join */ pthread_mutex_lock(&mux_async_veri); /* set timeout value, absolute time */ clock_gettime(CLOCK_REALTIME, &async_veri_timeout); async_veri_timeout.tv_sec += ASYNC_VERIFICATION_TIMEOUT_SECONDS; while (get_async_verification_finished() == false) { TDBG("Async verification not finished yet!\n"); ret = pthread_cond_timedwait(&cond_async_veri, \ &mux_async_veri, &async_veri_timeout); /* if timeout, cancel thread_async_verification */ if (ret == ETIMEDOUT) { ret = pthread_cancel(thread_async_id); if (ret != 0) TDBG("Cancellation of thread_async_veri" \ "fication failed. \n"); TDBG("Pthread_cond_timedwait TIMEOUT.\n"); break; } } pthread_mutex_unlock(&mux_async_veri); thread_async_join: ret = pthread_join(thread_async_id, NULL); if (ret != 0) TDBG("Failure in pthread_join(thread_async_id)\n"); return 0; } /* TODO: decode message as STPv2 * Input: * Return: */ static int stp_decoding(char *buf, int length) { TDBG("Decoding of message in %s().\n", __FUNCTION__); return 0; } /* TODO:to match ETB buf content against async.veri list * Input: * Return: */ static bool match_async_node(struct stm_test_run_node_type *this, \ const char *buf) { TDBG("Match ETB buf against asnyn.veri list.\n"); TDBG("Fake, match found! \n"); return true; } /* remove this node from async verification list * Input: * - pre: pointer to the previous node * - this: pointer to this node, which will be removed from the list * Return: * - 0: success * - -1: failure * Assumption: * - mutex_async_list must be acquired before calling this API. */ static int remove_async_verification_node(struct stm_test_run_node_type *pre, \ struct stm_test_run_node_type *this) { TDBG("ASYNC node remove id=0x%08lx.\n", (unsigned long)this); if (pre == NULL) { /* if this points to list head */ stm_async_veri_node_list = this->next_async; this->next_async = NULL; if (stm_async_veri_node_list == NULL) /* empty */ ptr_async_veri_node_last = NULL; return 0; } pre->next_async = this->next_async; this->next_async = NULL; /* are we removing the last node? */ if (ptr_async_veri_node_last == this) ptr_async_veri_node_last = pre; /* adjust last node pointer */ return 0; } /* Entry function of async verifcation thread */ void *thread_async_verification(void *param) { int ret = -1; int fd_ETB = -1; int fdmax = -1; fd_set fds; struct timeval tv; char buf[MAX_ETB_LINE_BUF_SIZE] = ""; struct stm_test_run_node_type *ptr_async_veri_current = NULL; struct stm_test_run_node_type *ptr_pre = NULL; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); TDBG("Thread Entry Point, %s()\n", __FUNCTION__); /* open ETB file for readonly */ fd_ETB = open(ETB_FILEPATH, O_RDONLY | O_SYNC); if (fd_ETB == -1) { printf("Failed to open ETB file for readonly.\n"); TDBG("Exit Thread %s()\n", __FUNCTION__); /* thread exit */ pthread_exit(NULL); return NULL; } /* acquire async.list mutex lock */ pthread_mutex_lock(&mux_async_list); while (true) { /* if async verification list is empty and test commands sent */ if ((stm_async_veri_node_list == NULL) && \ (all_commands_sent == true)) { TDBG("Async verification list is empty, and all test" \ "commands were sent.\n"); TDBG("No new async verification is expected.\n"); TDBG("Async verification thread exit.\n"); /* release async.list mutex lock */ pthread_mutex_unlock(&mux_async_list); /* broadcast a condition met */ pthread_mutex_lock(&mux_async_veri); set_async_verification_finished(true); pthread_cond_signal(&cond_async_veri); pthread_mutex_unlock(&mux_async_veri); break; } TDBG("Thread: %s, Line: %d\n", __FUNCTION__, __LINE__); /* release async.list mutex lock */ pthread_mutex_unlock(&mux_async_list); /* select, wait on ETB device */ FD_ZERO(&fds); FD_SET(fd_ETB, &fds); fdmax = fd_ETB + 1; tv.tv_sec = 1; /* * ASYNC_VERIFICATION_TIMEOUT_SECONDS; */ tv.tv_usec = 0; ret = select(fdmax, &fds, NULL, &fds, &tv); #ifdef LOCAL_TEST_STUB /* We don't want the loop run too fase when locally */ { struct timespec delay = {1, 0}; /* {sec, usec} */ nanosleep(&delay, NULL); } #endif /* LOCAL_TEST_STUB */ if (ret == -1) { printf("Error select(), %s\n", __FUNCTION__); /* acquire async.list mutex lock */ pthread_mutex_lock(&mux_async_list); continue; } else if (ret == 0) { printf("Timeout happend in waiting ETB select().\n"); /* acquire async.list mutex lock */ pthread_mutex_lock(&mux_async_list); continue; } /* read ETB */ if (FD_ISSET(fd_ETB, &fds)) { ret = read(fd_ETB, buf, MAX_ETB_LINE_BUF_SIZE); if (ret >= 0) { /* decode message as STPv2 */ stp_decoding(buf, ret); } #ifdef LOCAL_TEST_STUB buf[ret] = '\0'; TDBG("ETB read: %s<\n", buf); /* for local debugging purpose */ /* rewind fd_ETB to its beginning */ lseek(fd_ETB, 0, SEEK_SET); #endif /* LOCAL_TEST_STUB */ } /* acquire async.list mutex lock */ pthread_mutex_lock(&mux_async_list); /* go through async.list to find a match */ ptr_async_veri_current = stm_async_veri_node_list; while (ptr_async_veri_current != NULL) { /* TODO: should we call async verification API here? */ /* if a match is found */ if (match_async_node(ptr_async_veri_current, buf) == \ true) { /* update the related node */ ptr_async_veri_current->command_result->async_veri_result = ASYNC_VERIFICATION_SUCCESS; /* record time of async_verified */ ret = clock_gettime(CLOCK_REALTIME, &(ptr_async_veri_current->command_result->time_async_verified)); if (ret == -1) /* failure */ TDBG("Failed to read clock time.\n"); /* and remove it from the list */ remove_async_verification_node(ptr_pre, \ ptr_async_veri_current); break; } /* move to next async node */ ptr_pre = ptr_async_veri_current; ptr_async_veri_current = \ ptr_async_veri_current->next_async; } /* loop again */ } TDBG("Thread Exit, %s()\n", __FUNCTION__); /* close ETB file */ if (fd_ETB != -1) close(fd_ETB); /* thread exit */ pthread_exit(NULL); return NULL; } /* free resources usded by async verification */ void async_free_resources(void) { pthread_cond_destroy(&cond_async_veri); pthread_mutex_destroy(&mux_async_veri); pthread_mutex_destroy(&mux_async_list); return; } #ifdef ASYNC_VERI_TEST_STUB int loop = 3000; struct timespec ten_us = {0, 10000}; void thread(void) { int i, j; /* pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); */ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); printf("Sub before.\n"); for(i = 0; i < loop * 3; i++) { /* printf("Sub thread. i=%5d\n", i); */ for(j = 0; j < 100000; j ++); /* pthread_testcancel(); */ } /* printf("Sub after.\n"); */ return; } int main(void) { pthread_t id; int i,ret; int fd_ETB; fd_set fds; int fdmax; struct timeval tv; char buf[MAX_ETB_LINE_BUF_SIZE] = ""; /* open ETB file for readonly */ fd_ETB = open(ETB_FILEPATH, O_RDONLY | O_SYNC); if (fd_ETB == -1) { printf("Failed to open ETB file for readonly.\n"); TDBG("Exit Thread %s()\n", __FUNCTION__); return -1; } #if 0 ret = read(fd_ETB, buf, MAX_ETB_LINE_BUF_SIZE); if (ret >= 0) { /* decode message as STPv2 */ stp_decoding(buf, ret); } #ifdef LOCAL_TEST_STUB buf[ret] = '\0'; TDBG("ETB read: %s<\n", buf); /* for local debugging purpose */ /* rewind fd_ETB to its beginning */ lseek(fd_ETB, 0, SEEK_SET); #endif /* LOCAL_TEST_STUB */ ret = read(fd_ETB, buf, MAX_ETB_LINE_BUF_SIZE); if (ret >= 0) { /* decode message as STPv2 */ stp_decoding(buf, ret); } #ifdef LOCAL_TEST_STUB buf[ret] = '\0'; TDBG("ETB read: %s<\n", buf); /* for local debugging purpose */ /* rewind fd_ETB to its beginning */ lseek(fd_ETB, 0, SEEK_SET); #endif /* LOCAL_TEST_STUB */ FD_ZERO(&fds); FD_SET(fd_ETB, &fds); fdmax = fd_ETB + 1; #endif i = 0; while (i < 10) { i ++; /* select, wait on ETB device */ FD_ZERO(&fds); FD_SET(fd_ETB, &fds); fdmax = fd_ETB + 1; tv.tv_sec = 1; /* * ASYNC_VERIFICATION_TIMEOUT_SECONDS; */ tv.tv_usec = 0; ret = select(fdmax, &fds, NULL, NULL, &tv); if (ret == -1) { printf("Error select(), %s\n", __FUNCTION__); continue; } else if (ret == 0) { printf("Timeout happend in waiting ETB select().\n"); continue; } /* read ETB */ if (FD_ISSET(fd_ETB, &fds)) { ret = read(fd_ETB, buf, MAX_ETB_LINE_BUF_SIZE); if (ret >= 0) { /* decode message as STPv2 */ stp_decoding(buf, ret); } #ifdef LOCAL_TEST_STUB buf[ret] = '\0'; TDBG("ETB read: %s<\n", buf); /* for local debugging purpose */ /* rewind fd_ETB to its beginning */ lseek(fd_ETB, 0, SEEK_SET); #endif /* LOCAL_TEST_STUB */ } } ret = pthread_create(&id, NULL, (void *)thread, NULL); pthread_cancel(id); pthread_join(id,NULL); printf("Main joined.\n"); return 0; if (ret != 0){ printf("Create pthread error!\n"); exit(1); } for(i = 0; i < loop; i++) { printf("Main thread. i=%5d\n", i); nanosleep(&ten_us, NULL); } pthread_cancel(id); pthread_join(id,NULL); return (0); } #if 0 /* to calculate difference between two timespec * Input: * - start: start of timespec * - end: end of timespec * Return: * - diff between start and end in 'struct timespec' format */ /* TODO: double check return a struct, good? */ static struct timespec time_diff(const struct timespec start, \ const struct timespec end) { struct timespec temp; if ((end.tv_nsec - start.tv_nsec) < 0) { temp.tv_sec = end.tv_sec - start.tv_sec - 1; temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { temp.tv_sec = end.tv_sec - start.tv_sec; temp.tv_nsec = end.tv_nsec - start.tv_nsec; } return temp; } int main() { pthread_mutex_init(&mux, NULL); pthread_mutex_lock(&mux); TDBG("pthread_mutex testing.\n"); pthread_mutex_unlock(&mux); return 0; } #endif #endif /* ASYNC_VERI_TEST_STUB */