1570af302Sopenharmony_ci#include <pthread.h>
2570af302Sopenharmony_ci#include <semaphore.h>
3570af302Sopenharmony_ci#include <string.h>
4570af302Sopenharmony_ci#include "test.h"
5570af302Sopenharmony_ci
6570af302Sopenharmony_ci#define TESTC(c, m) ( (c) || (t_error("%s failed (" m ")\n", #c), 0) )
7570af302Sopenharmony_ci#define TESTR(r, f, m) ( \
8570af302Sopenharmony_ci	((r) = (f)) == 0 || (t_error("%s failed: %s (" m ")\n", #f, strerror(r)), 0) )
9570af302Sopenharmony_ci
10570af302Sopenharmony_cistatic void *start_async(void *arg)
11570af302Sopenharmony_ci{
12570af302Sopenharmony_ci	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
13570af302Sopenharmony_ci	sem_post(arg);
14570af302Sopenharmony_ci	for (;;);
15570af302Sopenharmony_ci	return 0;
16570af302Sopenharmony_ci}
17570af302Sopenharmony_ci
18570af302Sopenharmony_cistatic void cleanup1(void *arg)
19570af302Sopenharmony_ci{
20570af302Sopenharmony_ci	*(int *)arg = 1;
21570af302Sopenharmony_ci}
22570af302Sopenharmony_ci
23570af302Sopenharmony_cistatic void cleanup2(void *arg)
24570af302Sopenharmony_ci{
25570af302Sopenharmony_ci	*(int *)arg += 2;
26570af302Sopenharmony_ci}
27570af302Sopenharmony_ci
28570af302Sopenharmony_cistatic void cleanup3(void *arg)
29570af302Sopenharmony_ci{
30570af302Sopenharmony_ci	*(int *)arg += 3;
31570af302Sopenharmony_ci}
32570af302Sopenharmony_ci
33570af302Sopenharmony_cistatic void cleanup4(void *arg)
34570af302Sopenharmony_ci{
35570af302Sopenharmony_ci	*(int *)arg += 4;
36570af302Sopenharmony_ci}
37570af302Sopenharmony_ci
38570af302Sopenharmony_cistatic void *start_single(void *arg)
39570af302Sopenharmony_ci{
40570af302Sopenharmony_ci	pthread_cleanup_push(cleanup1, arg);
41570af302Sopenharmony_ci	sleep(3);
42570af302Sopenharmony_ci	pthread_cleanup_pop(0);
43570af302Sopenharmony_ci	return 0;
44570af302Sopenharmony_ci}
45570af302Sopenharmony_ci
46570af302Sopenharmony_cistatic void *start_nested(void *arg)
47570af302Sopenharmony_ci{
48570af302Sopenharmony_ci	int *foo = arg;
49570af302Sopenharmony_ci	pthread_cleanup_push(cleanup1, foo);
50570af302Sopenharmony_ci	pthread_cleanup_push(cleanup2, foo+1);
51570af302Sopenharmony_ci	pthread_cleanup_push(cleanup3, foo+2);
52570af302Sopenharmony_ci	pthread_cleanup_push(cleanup4, foo+3);
53570af302Sopenharmony_ci	sleep(3);
54570af302Sopenharmony_ci	pthread_cleanup_pop(0);
55570af302Sopenharmony_ci	pthread_cleanup_pop(0);
56570af302Sopenharmony_ci	pthread_cleanup_pop(0);
57570af302Sopenharmony_ci	pthread_cleanup_pop(0);
58570af302Sopenharmony_ci	return 0;
59570af302Sopenharmony_ci}
60570af302Sopenharmony_ci
61570af302Sopenharmony_ciint main(void)
62570af302Sopenharmony_ci{
63570af302Sopenharmony_ci	pthread_t td;
64570af302Sopenharmony_ci	sem_t sem1;
65570af302Sopenharmony_ci	int r;
66570af302Sopenharmony_ci	void *res;
67570af302Sopenharmony_ci	int foo[4];
68570af302Sopenharmony_ci
69570af302Sopenharmony_ci	TESTR(r, sem_init(&sem1, 0, 0), "creating semaphore");
70570af302Sopenharmony_ci
71570af302Sopenharmony_ci	/* Asynchronous cancellation */
72570af302Sopenharmony_ci	TESTR(r, pthread_create(&td, 0, start_async, &sem1), "failed to create thread");
73570af302Sopenharmony_ci	while (sem_wait(&sem1));
74570af302Sopenharmony_ci	TESTR(r, pthread_cancel(td), "canceling");
75570af302Sopenharmony_ci	TESTR(r, pthread_join(td, &res), "joining canceled thread");
76570af302Sopenharmony_ci	TESTC(res == PTHREAD_CANCELED, "canceled thread exit status");
77570af302Sopenharmony_ci
78570af302Sopenharmony_ci	/* Cancellation cleanup handlers */
79570af302Sopenharmony_ci	foo[0] = 0;
80570af302Sopenharmony_ci	TESTR(r, pthread_create(&td, 0, start_single, foo), "failed to create thread");
81570af302Sopenharmony_ci	TESTR(r, pthread_cancel(td), "cancelling");
82570af302Sopenharmony_ci	TESTR(r, pthread_join(td, &res), "joining canceled thread");
83570af302Sopenharmony_ci	TESTC(res == PTHREAD_CANCELED, "canceled thread exit status");
84570af302Sopenharmony_ci	TESTC(foo[0] == 1, "cleanup handler failed to run");
85570af302Sopenharmony_ci
86570af302Sopenharmony_ci	/* Nested cleanup handlers */
87570af302Sopenharmony_ci	memset(foo, 0, sizeof foo);
88570af302Sopenharmony_ci	TESTR(r, pthread_create(&td, 0, start_nested, foo), "failed to create thread");
89570af302Sopenharmony_ci	TESTR(r, pthread_cancel(td), "cancelling");
90570af302Sopenharmony_ci	TESTR(r, pthread_join(td, &res), "joining canceled thread");
91570af302Sopenharmony_ci	TESTC(res == PTHREAD_CANCELED, "canceled thread exit status");
92570af302Sopenharmony_ci	TESTC(foo[0] == 1, "cleanup handler failed to run");
93570af302Sopenharmony_ci	TESTC(foo[1] == 2, "cleanup handler failed to run");
94570af302Sopenharmony_ci	TESTC(foo[2] == 3, "cleanup handler failed to run");
95570af302Sopenharmony_ci	TESTC(foo[3] == 4, "cleanup handler failed to run");
96570af302Sopenharmony_ci
97570af302Sopenharmony_ci	return t_status;
98570af302Sopenharmony_ci}
99