summaryrefslogtreecommitdiffstats
path: root/src/pipe.c
blob: 5599fe018bff1e09befffddb6583cc0cfc5d31b7 (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
/*
 * Pipe management
 *
 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 */

#include <unistd.h>
#include <fcntl.h>

#include <haproxy/api.h>
#include <haproxy/global.h>
#include <haproxy/pipe-t.h>
#include <haproxy/pool.h>
#include <haproxy/thread.h>


DECLARE_STATIC_POOL(pool_head_pipe, "pipe", sizeof(struct pipe));

struct pipe *pipes_live = NULL; /* pipes which are still ready to use */

__decl_spinlock(pipes_lock); /* lock used to protect pipes list */

static THREAD_LOCAL int local_pipes_free = 0;  /* #cache objects   */
static THREAD_LOCAL struct pipe *local_pipes = NULL;

int pipes_used = 0;             /* # of pipes in use (2 fds each) */
int pipes_free = 0;             /* # of pipes unused */

/* return a pre-allocated empty pipe. Try to allocate one if there isn't any
 * left. NULL is returned if a pipe could not be allocated.
 */
struct pipe *get_pipe()
{
	struct pipe *ret = NULL;
	int pipefd[2];

	ret = local_pipes;
	if (likely(ret)) {
		local_pipes = ret->next;
		local_pipes_free--;
		HA_ATOMIC_DEC(&pipes_free);
		HA_ATOMIC_INC(&pipes_used);
		goto out;
	}

	if (likely(pipes_live)) {
		HA_SPIN_LOCK(PIPES_LOCK, &pipes_lock);
		ret = pipes_live;
		if (likely(ret))
			pipes_live = ret->next;
		HA_SPIN_UNLOCK(PIPES_LOCK, &pipes_lock);
		if (ret) {
			HA_ATOMIC_DEC(&pipes_free);
			HA_ATOMIC_INC(&pipes_used);
			goto out;
		}
	}

	HA_ATOMIC_INC(&pipes_used);
	if (pipes_used + pipes_free >= global.maxpipes)
		goto fail;

	ret = pool_alloc(pool_head_pipe);
	if (!ret)
		goto fail;

	if (pipe(pipefd) < 0)
		goto fail;

#ifdef F_SETPIPE_SZ
	if (global.tune.pipesize)
		fcntl(pipefd[0], F_SETPIPE_SZ, global.tune.pipesize);
#endif
	ret->data = 0;
	ret->prod = pipefd[1];
	ret->cons = pipefd[0];
	ret->next = NULL;
 out:
	return ret;
 fail:
	pool_free(pool_head_pipe, ret);
	HA_ATOMIC_DEC(&pipes_used);
	return NULL;

}

/* destroy a pipe, possibly because an error was encountered on it. Its FDs
 * will be closed and it will not be reinjected into the live pool.
 */
void kill_pipe(struct pipe *p)
{
	close(p->prod);
	close(p->cons);
	pool_free(pool_head_pipe, p);
	HA_ATOMIC_DEC(&pipes_used);
}

/* put back a unused pipe into the live pool. If it still has data in it, it is
 * closed and not reinjected into the live pool. The caller is not allowed to
 * use it once released.
 */
void put_pipe(struct pipe *p)
{
	if (unlikely(p->data)) {
		kill_pipe(p);
		return;
	}

	if (likely(local_pipes_free * global.nbthread < global.maxpipes - pipes_used)) {
		p->next = local_pipes;
		local_pipes = p;
		local_pipes_free++;
		goto out;
	}

	HA_SPIN_LOCK(PIPES_LOCK, &pipes_lock);
	p->next = pipes_live;
	pipes_live = p;
	HA_SPIN_UNLOCK(PIPES_LOCK, &pipes_lock);
 out:
	HA_ATOMIC_INC(&pipes_free);
	HA_ATOMIC_DEC(&pipes_used);
}

/*
 * Local variables:
 *  c-indent-level: 8
 *  c-basic-offset: 8
 * End:
 */