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