sna_threads.c revision 03b705cf
1/*
2 * Copyright © 2013 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Chris Wilson <chris@chris-wilson.co.uk>
25 *
26 */
27
28#include "sna.h"
29
30#include <unistd.h>
31#include <pthread.h>
32#include <signal.h>
33
34static int max_threads = -1;
35
36static struct thread {
37    pthread_t thread;
38    pthread_mutex_t mutex;
39    pthread_cond_t cond;
40
41    void (*func)(void *arg);
42    void *arg;
43} *threads;
44
45static void *__run__(void *arg)
46{
47	struct thread *t = arg;
48	sigset_t signals;
49
50	/* Disable all signals in the slave threads as X uses them for IO */
51	sigfillset(&signals);
52	pthread_sigmask(SIG_BLOCK, &signals, NULL);
53
54	pthread_mutex_lock(&t->mutex);
55	while (1) {
56		while (t->func == NULL)
57			pthread_cond_wait(&t->cond, &t->mutex);
58		pthread_mutex_unlock(&t->mutex);
59
60		assert(t->func);
61		t->func(t->arg);
62
63		pthread_mutex_lock(&t->mutex);
64		t->func = NULL;
65		pthread_cond_signal(&t->cond);
66	}
67	pthread_mutex_unlock(&t->mutex);
68
69	return NULL;
70}
71
72#if defined(__GNUC__)
73#define popcount(x) __builtin_popcount(x)
74#else
75static int popcount(unsigned int x)
76{
77	int count = 0;
78
79	while (x) {
80		count += x&1;
81		x >>= 1;
82	}
83
84	return count;
85}
86#endif
87
88static int
89num_cores(void)
90{
91	FILE *file = fopen("/proc/cpuinfo", "r");
92	int count = 0;
93	if (file) {
94		size_t len = 0;
95		char *line = NULL;
96		uint32_t processors = 0, cores = 0;
97		while (getline(&line, &len, file) != -1) {
98			int id;
99			if (sscanf(line, "physical id : %d", &id) == 1) {
100				if (id >= 32)
101					return 0;
102				processors |= 1 << id;
103			} else if (sscanf(line, "core id : %d", &id) == 1) {
104				if (id >= 32)
105					return 0;
106				cores |= 1 << id;
107			}
108		}
109		free(line);
110		fclose(file);
111
112		DBG(("%s: processors=0x%08x, cores=0x%08x\n",
113		     __FUNCTION__, processors, cores));
114
115		count = popcount(processors) * popcount(cores);
116	}
117	return count;
118}
119
120void sna_threads_init(void)
121{
122	int n;
123
124	if (max_threads != -1)
125		return;
126
127	max_threads = num_cores();
128	if (max_threads == 0)
129		max_threads = sysconf(_SC_NPROCESSORS_ONLN) / 2;
130	if (max_threads <= 1)
131		goto bail;
132
133	DBG(("%s: creating a thread pool of %d threads\n",
134	     __func__, max_threads));
135
136	threads = malloc (sizeof(threads[0])*max_threads);
137	if (threads == NULL)
138		goto bail;
139
140	for (n = 0; n < max_threads; n++) {
141		pthread_mutex_init(&threads[n].mutex, NULL);
142		pthread_cond_init(&threads[n].cond, NULL);
143
144		threads[n].func = NULL;
145		if (pthread_create(&threads[n].thread, NULL,
146				   __run__, &threads[n]))
147			goto bail;
148	}
149
150	return;
151
152bail:
153	max_threads = 0;
154}
155
156void sna_threads_run(void (*func)(void *arg), void *arg)
157{
158	int n;
159
160	assert(max_threads > 0);
161
162	for (n = 0; n < max_threads; n++) {
163		if (threads[n].func)
164			continue;
165
166		pthread_mutex_lock(&threads[n].mutex);
167		if (threads[n].func) {
168			pthread_mutex_unlock(&threads[n].mutex);
169			continue;
170		}
171
172		goto execute;
173	}
174
175	n = rand() % max_threads;
176	pthread_mutex_lock(&threads[n].mutex);
177	while (threads[n].func)
178		pthread_cond_wait(&threads[n].cond, &threads[n].mutex);
179
180execute:
181	threads[n].func = func;
182	threads[n].arg = arg;
183	pthread_cond_signal(&threads[n].cond);
184	pthread_mutex_unlock(&threads[n].mutex);
185}
186
187void sna_threads_wait(void)
188{
189	int n;
190
191	assert(max_threads > 0);
192
193	for (n = 0; n < max_threads; n++) {
194		if (threads[n].func == NULL)
195			continue;
196
197		pthread_mutex_lock(&threads[n].mutex);
198		while (threads[n].func)
199			pthread_cond_wait(&threads[n].cond, &threads[n].mutex);
200		pthread_mutex_unlock(&threads[n].mutex);
201	}
202}
203
204int sna_use_threads(int width, int height, int threshold)
205{
206	int num_threads;
207
208	if (max_threads <= 0)
209		return 1;
210
211	if (width < 128)
212		height /= 128/width;
213
214	num_threads = height * max_threads / threshold - 1;
215	if (num_threads <= 0)
216		return 1;
217
218	if (num_threads > max_threads)
219		num_threads = max_threads;
220	return num_threads;
221}
222
223struct thread_composite {
224	pixman_image_t *src, *mask, *dst;
225	pixman_op_t op;
226	int16_t src_x, src_y;
227	int16_t mask_x, mask_y;
228	int16_t dst_x, dst_y;
229	uint16_t width, height;
230};
231
232static void thread_composite(void *arg)
233{
234	struct thread_composite *t = arg;
235	pixman_image_composite(t->op, t->src, t->mask, t->dst,
236			       t->src_x, t->src_y,
237			       t->mask_x, t->mask_y,
238			       t->dst_x, t->dst_y,
239			       t->width, t->height);
240}
241
242void sna_image_composite(pixman_op_t        op,
243			 pixman_image_t    *src,
244			 pixman_image_t    *mask,
245			 pixman_image_t    *dst,
246			 int16_t            src_x,
247			 int16_t            src_y,
248			 int16_t            mask_x,
249			 int16_t            mask_y,
250			 int16_t            dst_x,
251			 int16_t            dst_y,
252			 uint16_t           width,
253			 uint16_t           height)
254{
255	int num_threads;
256
257	num_threads = sna_use_threads(width, height, 32);
258	if (num_threads <= 1) {
259		pixman_image_composite(op, src, mask, dst,
260				       src_x, src_y,
261				       mask_x, mask_y,
262				       dst_x, dst_y,
263				       width, height);
264	} else {
265		struct thread_composite data[num_threads];
266		int y, dy, n;
267
268		DBG(("%s: using %d threads for compositing %dx%d\n",
269		     __FUNCTION__, num_threads, width, height));
270
271		y = dst_y;
272		dy = (height + num_threads - 1) / num_threads;
273
274		data[0].op = op;
275		data[0].src = src;
276		data[0].mask = mask;
277		data[0].dst = dst;
278		data[0].src_x = src_x;
279		data[0].src_y = src_y;
280		data[0].mask_x = mask_x;
281		data[0].mask_y = mask_y;
282		data[0].dst_x = dst_x;
283		data[0].dst_y = y;
284		data[0].width = width;
285		data[0].height = dy;
286
287		for (n = 1; n < num_threads; n++) {
288			data[n] = data[0];
289			data[n].src_y += y - dst_y;
290			data[n].mask_y += y - dst_y;
291			data[n].dst_y = y;
292			y += dy;
293
294			sna_threads_run(thread_composite, &data[n]);
295		}
296
297		if (y + dy > dst_y + height)
298			dy = dst_y + height - y;
299
300		data[0].src_y += y - dst_y;
301		data[0].mask_y += y - dst_y;
302		data[0].dst_y = y;
303		data[0].height = dy;
304
305		thread_composite(&data[0]);
306
307		sna_threads_wait();
308	}
309}
310