File: thread-utils.c

package info (click to toggle)
vdo 8.3.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,536 kB
  • sloc: ansic: 21,023; sh: 349; makefile: 314; perl: 242
file content (176 lines) | stat: -rw-r--r-- 4,369 bytes parent folder | download | duplicates (2)
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
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2023 Red Hat
 */

#include "thread-utils.h"

#include <errno.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

#include "logger.h"
#include "memory-alloc.h"
#include "permassert.h"
#include "syscalls.h"

enum {
	ONCE_NOT_DONE = 0,
	ONCE_IN_PROGRESS = 1,
	ONCE_COMPLETE = 2,
};

/**********************************************************************/
unsigned int num_online_cpus(void)
{
	cpu_set_t cpu_set;
	unsigned int n_cpus = 0;
	unsigned int i;

	if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) != 0) {
		vdo_log_warning_strerror(errno,
					 "sched_getaffinity() failed, using 1 as number of cores.");
		return 1;
	}

	for (i = 0; i < CPU_SETSIZE; i++)
		n_cpus += CPU_ISSET(i, &cpu_set);
	return n_cpus;
}

/**********************************************************************/
void uds_get_thread_name(char *name)
{
	process_control(PR_GET_NAME, (unsigned long) name, 0, 0, 0);
}

/**********************************************************************/
pid_t uds_get_thread_id(void)
{
	return syscall(SYS_gettid);
}

/* Run a function once only, and record that fact in the atomic value. */
void vdo_perform_once(atomic_t *once, void (*function)(void))
{
	for (;;) {
		switch (atomic_cmpxchg(once, ONCE_NOT_DONE, ONCE_IN_PROGRESS)) {
		case ONCE_NOT_DONE:
			function();
			atomic_set_release(once, ONCE_COMPLETE);
			return;
		case ONCE_IN_PROGRESS:
			sched_yield();
			break;
		case ONCE_COMPLETE:
			return;
		default:
			return;
		}
	}
}

struct thread_start_info {
	void (*thread_function)(void *);
	void *thread_data;
	const char *name;
};

/**********************************************************************/
static void *thread_starter(void *arg)
{
	struct thread_start_info *info = arg;
	void (*thread_function)(void *) = info->thread_function;
	void *thread_data = info->thread_data;

	/*
	 * The name is just advisory for humans examining it, so we don't
	 * care much if this fails.
	 */
	process_control(PR_SET_NAME, (unsigned long) info->name, 0, 0, 0);
	vdo_free(info);
	thread_function(thread_data);
	return NULL;
}

/**********************************************************************/
int vdo_create_thread(void (*thread_function)(void *),
		      void *thread_data,
		      const char *name,
		      struct thread **new_thread)
{
	int result;
	struct thread_start_info *info;
	struct thread *thread;

	result = vdo_allocate(1, struct thread_start_info, __func__, &info);
	if (result != VDO_SUCCESS)
		return result;
	info->thread_function = thread_function;
	info->thread_data = thread_data;
	info->name = name;

	result = vdo_allocate(1, struct thread, __func__, &thread);
	if (result != VDO_SUCCESS) {
		vdo_log_warning("Error allocating memory for %s", name);
		vdo_free(info);
		return result;
	}

	result = pthread_create(&thread->thread, NULL, thread_starter, info);
	if (result != 0) {
		result = -errno;
		vdo_log_error_strerror(result, "could not create %s thread",
				       name);
		vdo_free(thread);
		vdo_free(info);
		return result;
	}

	*new_thread = thread;
	return VDO_SUCCESS;
}

/**********************************************************************/
void vdo_join_threads(struct thread *thread)
{
	int result;
	pthread_t pthread;

	result = pthread_join(thread->thread, NULL);
	pthread = thread->thread;
	vdo_free(thread);
	VDO_ASSERT_LOG_ONLY((result == 0), "thread: %p", (void *) pthread);
}

/**********************************************************************/
void initialize_threads_barrier(struct threads_barrier *barrier,
				unsigned int thread_count)
{
	int result;

	result = pthread_barrier_init(&barrier->barrier, NULL, thread_count);
	VDO_ASSERT_LOG_ONLY((result == 0), "pthread_barrier_init error");
}

/**********************************************************************/
void destroy_threads_barrier(struct threads_barrier *barrier)
{
	int result;

	result = pthread_barrier_destroy(&barrier->barrier);
	VDO_ASSERT_LOG_ONLY((result == 0), "pthread_barrier_destroy error");
}

/**********************************************************************/
void enter_threads_barrier(struct threads_barrier *barrier)
{
	int result;

	result = pthread_barrier_wait(&barrier->barrier);
	if (result == PTHREAD_BARRIER_SERIAL_THREAD)
		return;

	VDO_ASSERT_LOG_ONLY((result == 0), "pthread_barrier_wait error");
}