]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libfrog/ptvar.c
xfs: hoist xfs_scrub_agfl_walk to libxfs as xfs_agfl_walk
[thirdparty/xfsprogs-dev.git] / libfrog / ptvar.c
CommitLineData
d11cc69e
DW
1/*
2 * Copyright (C) 2018 Oracle. All Rights Reserved.
3 *
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20#include <stdint.h>
21#include <stdlib.h>
22#include <stdbool.h>
23#include <string.h>
24#include <assert.h>
25#include <pthread.h>
26#include <unistd.h>
27#include "platform_defs.h"
28#include "ptvar.h"
29
30/*
31 * Per-thread Variables
32 *
33 * This data structure manages a lockless per-thread variable. We
34 * implement this by allocating an array of memory regions, and as each
35 * thread tries to acquire its own region, we hand out the array
36 * elements to each thread. This way, each thread gets its own
37 * cacheline and (after the first access) doesn't have to contend for a
38 * lock for each access.
39 */
40struct ptvar {
41 pthread_key_t key;
42 pthread_mutex_t lock;
43 size_t nr_used;
44 size_t nr_counters;
45 size_t data_size;
46 unsigned char data[0];
47};
48#define PTVAR_SIZE(nr, sz) (sizeof(struct ptvar) + ((nr) * (size)))
49
50/* Initialize per-thread counter. */
51struct ptvar *
52ptvar_init(
53 size_t nr,
54 size_t size)
55{
56 struct ptvar *ptv;
57 int ret;
58
59#ifdef _SC_LEVEL1_DCACHE_LINESIZE
60 /* Try to prevent cache pingpong by aligning to cacheline size. */
61 size = max(size, sysconf(_SC_LEVEL1_DCACHE_LINESIZE));
62#endif
63
64 ptv = malloc(PTVAR_SIZE(nr, size));
65 if (!ptv)
66 return NULL;
67 ptv->data_size = size;
68 ptv->nr_counters = nr;
69 ptv->nr_used = 0;
70 memset(ptv->data, 0, nr * size);
71 ret = pthread_mutex_init(&ptv->lock, NULL);
72 if (ret)
73 goto out;
74 ret = pthread_key_create(&ptv->key, NULL);
75 if (ret)
76 goto out_mutex;
77 return ptv;
78
79out_mutex:
80 pthread_mutex_destroy(&ptv->lock);
81out:
82 free(ptv);
83 return NULL;
84}
85
86/* Free per-thread counter. */
87void
88ptvar_free(
89 struct ptvar *ptv)
90{
91 pthread_key_delete(ptv->key);
92 pthread_mutex_destroy(&ptv->lock);
93 free(ptv);
94}
95
96/* Get a reference to this thread's variable. */
97void *
98ptvar_get(
99 struct ptvar *ptv)
100{
101 void *p;
102
103 p = pthread_getspecific(ptv->key);
104 if (!p) {
105 pthread_mutex_lock(&ptv->lock);
106 assert(ptv->nr_used < ptv->nr_counters);
107 p = &ptv->data[(ptv->nr_used++) * ptv->data_size];
108 pthread_setspecific(ptv->key, p);
109 pthread_mutex_unlock(&ptv->lock);
110 }
111 return p;
112}
113
114/* Iterate all of the per-thread variables. */
115bool
116ptvar_foreach(
117 struct ptvar *ptv,
118 ptvar_iter_fn fn,
119 void *foreach_arg)
120{
121 size_t i;
122 bool ret = true;
123
124 pthread_mutex_lock(&ptv->lock);
125 for (i = 0; i < ptv->nr_used; i++) {
126 ret = fn(ptv, &ptv->data[i * ptv->data_size], foreach_arg);
127 if (!ret)
128 break;
129 }
130 pthread_mutex_unlock(&ptv->lock);
131
132 return ret;
133}