xref: /redis-3.2.3/src/bio.c (revision 8ea2dfd7)
1 /* Background I/O service for Redis.
2  *
3  * This file implements operations that we need to perform in the background.
4  * Currently there is only a single operation, that is a background close(2)
5  * system call. This is needed as when the process is the last owner of a
6  * reference to a file closing it means unlinking it, and the deletion of the
7  * file is slow, blocking the server.
8  *
9  * In the future we'll either continue implementing new things we need or
10  * we'll switch to libeio. However there are probably long term uses for this
11  * file as we may want to put here Redis specific background tasks (for instance
12  * it is not impossible that we'll need a non blocking FLUSHDB/FLUSHALL
13  * implementation).
14  *
15  * DESIGN
16  * ------
17  *
18  * The design is trivial, we have a structure representing a job to perform
19  * and a single thread performing all the I/O operations in the queue.
20  * Currently there is no way for the creator of the job to be notified about
21  * the completion of the operation, this will only be added when/if needed.
22  */
23 
24 #include "redis.h"
25 #include "bio.h"
26 
27 static pthread_mutex_t bio_mutex;
28 static pthread_cond_t bio_condvar;
29 list *bio_jobs;
30 
31 /* This structure represents a background Job. It is only used locally to this
32  * file as the API deos not expose the internals at all. */
33 struct bio_job {
34     int type;       /* Job type, for instance BIO_JOB_CLOSE */
35     void *data;     /* Job specific arguments pointer. */
36 };
37 
38 void *bioProcessBackgroundJobs(void *arg);
39 
40 /* Make sure we have enough stack to perform all the things we do in the
41  * main thread. */
42 #define REDIS_THREAD_STACK_SIZE (1024*1024*4)
43 
44 /* Initialize the background system, spawning the thread. */
45 void bioInit(void) {
46     pthread_attr_t attr;
47     pthread_t thread;
48     size_t stacksize;
49 
50     pthread_mutex_init(&bio_mutex,NULL);
51     pthread_cond_init(&bio_condvar,NULL);
52     bio_jobs = listCreate();
53 
54     /* Set the stack size as by default it may be small in some system */
55     pthread_attr_init(&attr);
56     pthread_attr_getstacksize(&attr,&stacksize);
57     if (!stacksize) stacksize = 1; /* The world is full of Solaris Fixes */
58     while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
59     pthread_attr_setstacksize(&attr, stacksize);
60 
61     /* Ready to spawn our thread */
62     if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,NULL) != 0) {
63         redisLog(REDIS_WARNING,"Fatal: Can't initialize Background Jobs.");
64         exit(1);
65     }
66 }
67 
68 void bioCreateBackgroundJob(int type, void *data) {
69     struct bio_job *job = zmalloc(sizeof(*job));
70 
71     job->type = type;
72     job->data = data;
73     pthread_mutex_lock(&bio_mutex);
74     listAddNodeTail(bio_jobs,job);
75     pthread_mutex_unlock(&bio_mutex);
76 }
77 
78 void *bioProcessBackgroundJobs(void *arg) {
79     struct bio_job *job;
80     REDIS_NOTUSED(arg);
81 
82     pthread_detach(pthread_self());
83     pthread_mutex_lock(&bio_mutex);
84     while(1) {
85         listNode *ln;
86 
87         /* The loop always starts with the lock hold. */
88         if (listLength(bio_jobs) == 0) {
89             pthread_cond_wait(&bio_condvar,&bio_mutex);
90             continue;
91         }
92         /* Pop the job from the queue. */
93         ln = listFirst(bio_jobs);
94         job = ln->value;
95         listDelNode(bio_jobs,ln);
96         /* It is now possible to unlock the background system as we know have
97          * a stand alone job structure to process.*/
98         pthread_mutex_unlock(&bio_mutex);
99 
100         /* Process the job accordingly to its type. */
101         if (job->type == REDIS_BIO_CLOSE_FILE) {
102             printf("Closing file...\n");
103             close((long)job->data);
104         } else {
105             redisPanic("Wrong job type in bioProcessBackgroundJobs().");
106         }
107         zfree(job);
108 
109         /* Lock again before reiterating the loop, if there are no longer
110          * jobs to process we'll block again in pthread_cond_wait(). */
111         pthread_mutex_lock(&bio_mutex);
112     }
113 }
114