summaryrefslogtreecommitdiff
path: root/async_veri.c
blob: c85a9a91f1ca81f87d5fb9a95d126a30862f8fdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
/* ------------------------------------------------------------------
 * Asynchorous verification related variables and utilities
 *
 * -----------------------------------------------------------------*/
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>

#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 */