struct Stack* parseCmdLine(const char* const clStr)
{
	struct CmdLine* cmdLine;
	struct Queue* tokens;
	char *token, *nextToken, *command;
	char **operators, **files;
	bool background, pushed, pipedIn, pipeOut;
	struct Stack* commandStack;
	int i;
	struct LinkedList* parameters;

	tokens=tokenize(clStr);
	commandStack=stackCreate();

	pipedIn=false;
	pipeOut=false;

	while (!queueIsEmpty(tokens))
	{
		//Get Command
		pushed=false;
		command=(char *)queueGetFront(tokens);

		//Parse Parameters
		parameters=listCreate();
		if (!queueIsEmpty(tokens))
		{
			while (!queueIsEmpty(tokens))
			{
				token=(char *)queuePeek(tokens);
				if (!isDelimiter(token))
				{
					listAppend(parameters, token);
					queueGetFront(tokens);
				}
				else
				{
					break;
				}
			}
		}

		//Check for Redirects
		operators=(char **)malloc(sizeof(char*)*REDIRECTS);
		files=(char **)malloc(sizeof(char*)*REDIRECTS);
		for (i=0; i<REDIRECTS; i++)
		{
			operators[i]=NULL;
			files[i]=NULL;
		}
		if (!queueIsEmpty(tokens))
		{
			for (i=0; i<REDIRECTS && !queueIsEmpty(tokens); i++)
			{
				token=(char *)queuePeek(tokens);

				if (strcmp(token, CL_TOKEN_GREATER_THAN)==0
                                    || strcmp(token, CL_TOKEN_LESS_THAN)==0)
				{
					operators[i]=token;
					queueGetFront(tokens);
					token=(char *)queueGetFront(tokens);
					files[i]=token;
				}
				else
				{
					break;
				}
			}
		}

		//Check for background process or pipe
		background=false;
		pipeOut=false;
		if (!queueIsEmpty(tokens))
		{
			token=(char *)queuePeek(tokens);
			if (strcmp(token, CL_TOKEN_AMP)==0)
			{
				queueGetFront(tokens);
				background=true;
			}
			else if (strcmp(token, CL_TOKEN_PIPE)==0)
                        {
				queueGetFront(tokens);
                                pipeOut=true;
                        }
		}
		
		//Add Command to Stack of Commands
		pushCmdLine(commandStack, command, parameters, operators, files, pipedIn, pipeOut, background);
		pushed=true;
		pipedIn=pipeOut;

		if (background || !pipeOut)
		{
			break; //Terminated because in background or syntax error
		}
	}

	if (!pushed)
	{
		free(operators);
		free(files);
	}
	
        queueDestroy(tokens, false);

	return commandStack;
}
Esempio n. 2
0
/* The SORT command is the most complex command in Redis. Warning: this code
 * is optimized for speed and a bit less for readability */
void sortCommand(redisClient *c) {
    list *operations;
    unsigned int outputlen = 0;
    int desc = 0, alpha = 0;
    long limit_start = 0, limit_count = -1, start, end;
    int j, dontsort = 0, vectorlen;
    int getop = 0; /* GET operation counter */
    int int_convertion_error = 0;
    int syntax_error = 0;
    robj *sortval, *sortby = NULL, *storekey = NULL;
    redisSortObject *vector; /* Resulting vector to sort */

    /* Lookup the key to sort. It must be of the right types */
    sortval = lookupKeyRead(c->db,c->argv[1]);
    if (sortval && sortval->type != REDIS_SET &&
                   sortval->type != REDIS_LIST &&
                   sortval->type != REDIS_ZSET)
    {
        addReply(c,shared.wrongtypeerr);
        return;
    }

    /* Create a list of operations to perform for every sorted element.
     * Operations can be GET */
    operations = listCreate();
    listSetFreeMethod(operations,zfree);
    j = 2; /* options start at argv[2] */

    /* Now we need to protect sortval incrementing its count, in the future
     * SORT may have options able to overwrite/delete keys during the sorting
     * and the sorted key itself may get destroyed */
    if (sortval)
        incrRefCount(sortval);
    else
        sortval = createQuicklistObject();

    /* The SORT command has an SQL-alike syntax, parse it */
    while(j < c->argc) {
        int leftargs = c->argc-j-1;
        if (!strcasecmp(c->argv[j]->ptr,"asc")) {
            desc = 0;
        } else if (!strcasecmp(c->argv[j]->ptr,"desc")) {
            desc = 1;
        } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) {
            alpha = 1;
        } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) {
            if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL)
                 != REDIS_OK) ||
                (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL)
                 != REDIS_OK))
            {
                syntax_error++;
                break;
            }
            j+=2;
        } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) {
            storekey = c->argv[j+1];
            j++;
        } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) {
            sortby = c->argv[j+1];
            /* If the BY pattern does not contain '*', i.e. it is constant,
             * we don't need to sort nor to lookup the weight keys. */
            if (strchr(c->argv[j+1]->ptr,'*') == NULL) {
                dontsort = 1;
            } else {
                /* If BY is specified with a real patter, we can't accept
                 * it in cluster mode. */
                if (server.cluster_enabled) {
                    addReplyError(c,"BY option of SORT denied in Cluster mode.");
                    syntax_error++;
                    break;
                }
            }
            j++;
        } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {
            if (server.cluster_enabled) {
                addReplyError(c,"GET option of SORT denied in Cluster mode.");
                syntax_error++;
                break;
            }
            listAddNodeTail(operations,createSortOperation(
                REDIS_SORT_GET,c->argv[j+1]));
            getop++;
            j++;
        } else {
            addReply(c,shared.syntaxerr);
            syntax_error++;
            break;
        }
        j++;
    }

    /* Handle syntax errors set during options parsing. */
    if (syntax_error) {
        decrRefCount(sortval);
        listRelease(operations);
        return;
    }

    /* When sorting a set with no sort specified, we must sort the output
     * so the result is consistent across scripting and replication.
     *
     * The other types (list, sorted set) will retain their native order
     * even if no sort order is requested, so they remain stable across
     * scripting and replication. */
    if (dontsort &&
        sortval->type == REDIS_SET &&
        (storekey || c->flags & REDIS_LUA_CLIENT))
    {
        /* Force ALPHA sorting */
        dontsort = 0;
        alpha = 1;
        sortby = NULL;
    }

    /* Destructively convert encoded sorted sets for SORT. */
    if (sortval->type == REDIS_ZSET)
        zsetConvert(sortval, REDIS_ENCODING_SKIPLIST);

    /* Objtain the length of the object to sort. */
    switch(sortval->type) {
    case REDIS_LIST: vectorlen = listTypeLength(sortval); break;
    case REDIS_SET: vectorlen =  setTypeSize(sortval); break;
    case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
    default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */
    }

    /* Perform LIMIT start,count sanity checking. */
    start = (limit_start < 0) ? 0 : limit_start;
    end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
    if (start >= vectorlen) {
        start = vectorlen-1;
        end = vectorlen-2;
    }
    if (end >= vectorlen) end = vectorlen-1;

    /* Whenever possible, we load elements into the output array in a more
     * direct way. This is possible if:
     *
     * 1) The object to sort is a sorted set or a list (internally sorted).
     * 2) There is nothing to sort as dontsort is true (BY <constant string>).
     *
     * In this special case, if we have a LIMIT option that actually reduces
     * the number of elements to fetch, we also optimize to just load the
     * range we are interested in and allocating a vector that is big enough
     * for the selected range length. */
    if ((sortval->type == REDIS_ZSET || sortval->type == REDIS_LIST) &&
        dontsort &&
        (start != 0 || end != vectorlen-1))
    {
        vectorlen = end-start+1;
    }

    /* Load the sorting vector with all the objects to sort */
    vector = zmalloc(sizeof(redisSortObject)*vectorlen);
    j = 0;

    if (sortval->type == REDIS_LIST && dontsort) {
        /* Special handling for a list, if 'dontsort' is true.
         * This makes sure we return elements in the list original
         * ordering, accordingly to DESC / ASC options.
         *
         * Note that in this case we also handle LIMIT here in a direct
         * way, just getting the required range, as an optimization. */
        if (end >= start) {
            listTypeIterator *li;
            listTypeEntry entry;
            li = listTypeInitIterator(sortval,
                    desc ? (long)(listTypeLength(sortval) - start - 1) : start,
                    desc ? REDIS_HEAD : REDIS_TAIL);

            while(j < vectorlen && listTypeNext(li,&entry)) {
                vector[j].obj = listTypeGet(&entry);
                vector[j].u.score = 0;
                vector[j].u.cmpobj = NULL;
                j++;
            }
            listTypeReleaseIterator(li);
            /* Fix start/end: output code is not aware of this optimization. */
            end -= start;
            start = 0;
        }
    } else if (sortval->type == REDIS_LIST) {
        listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL);
        listTypeEntry entry;
        while(listTypeNext(li,&entry)) {
            vector[j].obj = listTypeGet(&entry);
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
        }
        listTypeReleaseIterator(li);
    } else if (sortval->type == REDIS_SET) {
        setTypeIterator *si = setTypeInitIterator(sortval);
        robj *ele;
        while((ele = setTypeNextObject(si)) != NULL) {
            vector[j].obj = ele;
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
        }
        setTypeReleaseIterator(si);
    } else if (sortval->type == REDIS_ZSET && dontsort) {
        /* Special handling for a sorted set, if 'dontsort' is true.
         * This makes sure we return elements in the sorted set original
         * ordering, accordingly to DESC / ASC options.
         *
         * Note that in this case we also handle LIMIT here in a direct
         * way, just getting the required range, as an optimization. */

        zset *zs = sortval->ptr;
        zskiplist *zsl = zs->zsl;
        zskiplistNode *ln;
        robj *ele;
        int rangelen = vectorlen;

        /* Check if starting point is trivial, before doing log(N) lookup. */
        if (desc) {
            long zsetlen = dictSize(((zset*)sortval->ptr)->dict);

            ln = zsl->tail;
            if (start > 0)
                ln = zslGetElementByRank(zsl,zsetlen-start);
        } else {
            ln = zsl->header->level[0].forward;
            if (start > 0)
                ln = zslGetElementByRank(zsl,start+1);
        }

        while(rangelen--) {
            redisAssertWithInfo(c,sortval,ln != NULL);
            ele = ln->obj;
            vector[j].obj = ele;
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
            ln = desc ? ln->backward : ln->level[0].forward;
        }
        /* Fix start/end: output code is not aware of this optimization. */
        end -= start;
        start = 0;
    } else if (sortval->type == REDIS_ZSET) {
        dict *set = ((zset*)sortval->ptr)->dict;
        dictIterator *di;
        dictEntry *setele;
        di = dictGetIterator(set);
        while((setele = dictNext(di)) != NULL) {
            vector[j].obj = dictGetKey(setele);
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
        }
        dictReleaseIterator(di);
    } else {
        redisPanic("Unknown type");
    }
    redisAssertWithInfo(c,sortval,j == vectorlen);

    /* Now it's time to load the right scores in the sorting vector */
    if (dontsort == 0) {
        for (j = 0; j < vectorlen; j++) {
            robj *byval;
            if (sortby) {
                /* lookup value to sort by */
                byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
                if (!byval) continue;
            } else {
                /* use object itself to sort by */
                byval = vector[j].obj;
            }

            if (alpha) {
                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);
            } else {
                if (sdsEncodedObject(byval)) {
                    char *eptr;

                    vector[j].u.score = strtod(byval->ptr,&eptr);
                    if (eptr[0] != '\0' || errno == ERANGE ||
                        isnan(vector[j].u.score))
                    {
                        int_convertion_error = 1;
                    }
                } else if (byval->encoding == REDIS_ENCODING_INT) {
                    /* Don't need to decode the object if it's
                     * integer-encoded (the only encoding supported) so
                     * far. We can just cast it */
                    vector[j].u.score = (long)byval->ptr;
                } else {
                    redisAssertWithInfo(c,sortval,1 != 1);
                }
            }

            /* when the object was retrieved using lookupKeyByPattern,
             * its refcount needs to be decreased. */
            if (sortby) {
                decrRefCount(byval);
            }
        }
    }

    if (dontsort == 0) {
        server.sort_desc = desc;
        server.sort_alpha = alpha;
        server.sort_bypattern = sortby ? 1 : 0;
        server.sort_store = storekey ? 1 : 0;
        if (sortby && (start != 0 || end != vectorlen-1))
            pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
        else
            qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
    }

    /* Send command output to the output buffer, performing the specified
     * GET/DEL/INCR/DECR operations if any. */
    outputlen = getop ? getop*(end-start+1) : end-start+1;
    if (int_convertion_error) {
        addReplyError(c,"One or more scores can't be converted into double");
    } else if (storekey == NULL) {
        /* STORE option not specified, sent the sorting result to client */
        addReplyMultiBulkLen(c,outputlen);
        for (j = start; j <= end; j++) {
            listNode *ln;
            listIter li;

            if (!getop) addReplyBulk(c,vector[j].obj);
            listRewind(operations,&li);
            while((ln = listNext(&li))) {
                redisSortOperation *sop = ln->value;
                robj *val = lookupKeyByPattern(c->db,sop->pattern,
                    vector[j].obj);

                if (sop->type == REDIS_SORT_GET) {
                    if (!val) {
                        addReply(c,shared.nullbulk);
                    } else {
                        addReplyBulk(c,val);
                        decrRefCount(val);
                    }
                } else {
                    /* Always fails */
                    redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET);
                }
            }
        }
    } else {
        robj *sobj = createQuicklistObject();

        /* STORE option specified, set the sorting result as a List object */
        for (j = start; j <= end; j++) {
            listNode *ln;
            listIter li;

            if (!getop) {
                listTypePush(sobj,vector[j].obj,REDIS_TAIL);
            } else {
                listRewind(operations,&li);
                while((ln = listNext(&li))) {
                    redisSortOperation *sop = ln->value;
                    robj *val = lookupKeyByPattern(c->db,sop->pattern,
                        vector[j].obj);

                    if (sop->type == REDIS_SORT_GET) {
                        if (!val) val = createStringObject("",0);

                        /* listTypePush does an incrRefCount, so we should take care
                         * care of the incremented refcount caused by either
                         * lookupKeyByPattern or createStringObject("",0) */
                        listTypePush(sobj,val,REDIS_TAIL);
                        decrRefCount(val);
                    } else {
                        /* Always fails */
                        redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET);
                    }
                }
            }
        }
        if (outputlen) {
            setKey(c->db,storekey,sobj);
            notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"sortstore",storekey,
                                c->db->id);
            server.dirty += outputlen;
        } else if (dbDelete(c->db,storekey)) {
            signalModifiedKey(c->db,storekey);
            notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",storekey,c->db->id);
            server.dirty++;
        }
        decrRefCount(sobj);
        addReplyLongLong(c,outputlen);
    }

    /* Cleanup */
    if (sortval->type == REDIS_LIST || sortval->type == REDIS_SET)
        for (j = 0; j < vectorlen; j++)
            decrRefCount(vector[j].obj);
    decrRefCount(sortval);
    listRelease(operations);
    for (j = 0; j < vectorlen; j++) {
        if (alpha && vector[j].u.cmpobj)
            decrRefCount(vector[j].u.cmpobj);
    }
    zfree(vector);
}
Esempio n. 3
0
vnode_t *vnode_new(const char *root_dir, uint32_t id, enum eVnodeStorageType storage_type, vnode_write_queue_handle_write_cb vnode_write_queue_handle_write)
{
    vnode_t *vnode = (vnode_t*)zmalloc(sizeof(vnode_t));

    memset(vnode, 0, sizeof(vnode_t));
    vnode->id = id;
    vnode->storage_type = storage_type;
    /*vnode->max_dbsize = 1024L * 1024L * 1024L * 4L;*/
    vnode->max_dbsize = 1024L * 1024L * 500L;

    /* Create vnode root dir */
    sprintf(vnode->root_dir, "%s/%04d", root_dir, id);
    if ( mkdir_if_not_exist(vnode->root_dir) != 0 ){
        error_log("Cann't create vnode(%d) dir:%s", id, vnode->root_dir);
        zfree(vnode);
        return NULL;
    }

    /* datazones */
    int i;
    for ( i = 0 ; i < MAX_DATAZONES ; i++ ){
        vnode->datazones[i] = (datazone_t*)zmalloc(sizeof(datazone_t));
        if ( datazone_init(vnode->datazones[i], vnode, i) != 0 ){
            zfree(vnode);
            return NULL;
        }
    }

    /* Slices DB */
    if ( vnode->storage_type >= STORAGE_KVDB ){

        // Create Metadata DB.
        const char *metadata_dbname = "metadata";
        kvdb_t *kvdb_metadata = vnode_open_kvdb(vnode, metadata_dbname);
        if ( kvdb_metadata == NULL ){
            error_log("MetadataDB create failed. dbname:%s", metadata_dbname);
            zfree(vnode);
            return NULL;
        }
        vnode->kvdb_metadata = kvdb_metadata;

        uint32_t active_slicedb_id = 0;
        if ( kvdb_get_uint32(kvdb_metadata, "active_slicedb_id", &active_slicedb_id) != 0 ){
            active_slicedb_id = 0;
        }
        notice_log("vnode active_slicedb_id:%d", active_slicedb_id);

        // Create Slice DB.
        for ( int db_id = 0 ; db_id <= active_slicedb_id ; db_id++ ){
            slicedb_t *slicedb = vnode_open_slicedb(vnode, db_id);
            if ( slicedb != NULL ){
            } else {
            /*char dbname[NAME_MAX];*/
            /*sprintf(dbname, "slice-%03d", db_id);*/
            /*kvdb_t *kvdb = vnode_open_kvdb(vnode, dbname);*/
            /*if ( kvdb != NULL ){*/
                /*vnode->slicedbs[db_id] = slicedb_new(db_id, kvdb, vnode->max_dbsize);*/
            /*} else {*/
                /*error_log("SliceDB create failed. dbname:%s", dbname);*/
                for ( int n = 0 ; n < db_id ; n++ ){
                    slicedb_free(vnode->slicedbs[n]);
                    vnode->slicedbs[n] = NULL;
                }
                zfree(vnode);
                return NULL;
            }
        }
        vnode->active_slicedb = vnode->slicedbs[active_slicedb_id];
        /*vnode->kvdb = vnode->active_slicedb->kvdb;*/

    }

    vnode->caching_objects = object_queue_new(object_compare_md5_func);

    vnode->received_objects = listCreate();
    vnode->received_object_size = 0;
    vnode->standby_objects = listCreate();
    vnode->standby_object_size = 0;

    vnode->write_queue = init_work_queue(vnode_write_queue_handle_write, VNODE_WRITE_QUEUE_INTERVAL);

    return vnode;
}
Esempio n. 4
0
int main(int argc, char **argv)
{
	int			i, maxnconn;
	pthread_t	tid;
	struct file	*fptr;
	int current;
	listNode *listNode;

	for (i = 0; i < MAXFILES; i++) {
		file[i].f_flags = 0;
	}
	context = connectRedis(localhost,port);
	queue = listCreate();
	home_page(HOST, "/");

	nlefttoread = nlefttoconn = queue->len;
	nconn = 0;
	
	while (nlefttoconn > 0 || nconn > 0) {
		printf("lefttoconn: %d, conn:%d\n",nlefttoconn,nconn);
		current = 0;
		while (nconn < MAXFILES && nlefttoconn > 0) {
			for (i = current ; i < MAXFILES; i++)
				if (file[i].f_flags == 0)
				{
					current = i;
					break;
				}

			pthread_mutex_lock(&queue_mutex);
			listNode = queue->head;
			if (listNode == NULL)
			{
				printf("have some but no!\n");
				exit(1);
			}
			file[i].f_flags = F_CONNECTING;
			setFileNameAndHost(listNode->value,file[i].f_name,file[i].f_host);
			listDelNodeHead(queue);
			pthread_create(&tid, NULL, &do_get_read, &file[i]);
			file[i].f_tid = tid;
			nconn++;
			nlefttoconn = queue->len;
			pthread_mutex_unlock(&queue_mutex);
		}

		pthread_mutex_lock(&ndone_mutex);
		while (ndone == 0)
			pthread_cond_wait(&ndone_cond, &ndone_mutex);

		for (i = 0; i < MAXFILES; i++) {
			if (file[i].f_flags & F_DONE) {
				pthread_join(file[i].f_tid, (void **) &fptr);

				if (&file[i] != fptr)
				{
					printf("file[i]!=ptr\n");
					exit(1);
				}
				fptr->f_flags = 0;	/* clears F_DONE */
				ndone--;
				nconn--;
				printf("thread %d for name:%s host:%s done\n", fptr->f_tid, fptr->f_name,fptr->f_host);
			}
		}
		pthread_mutex_unlock(&ndone_mutex);
	}

	listRelease(queue);
	disconnectRedis(context);
	exit(0);
}
/* Watch for the specified key 
 *
 * 让客户端 c 监视给定的键 key
 */
void watchForKey(redisClient *c, robj *key) {

    list *clients = NULL;
    listIter li;
    listNode *ln;
    watchedKey *wk;

    /* Check if we are already watching for this key */
    // 检查 key 是否已经保存在 watched_keys 链表中,
    // 如果是的话,直接返回
    listRewind(c->watched_keys,&li);
    while((ln = listNext(&li))) {
        wk = listNodeValue(ln);
        if (wk->db == c->db && equalStringObjects(key,wk->key))
            return; /* Key already watched */
    }

    // 键不存在于 watched_keys ,添加它

    // 以下是一个 key 不存在于字典的例子:
    // before :
    // {
    //  'key-1' : [c1, c2, c3],
    //  'key-2' : [c1, c2],
    // }
    // after c-10086 WATCH key-1 and key-3:
    // {
    //  'key-1' : [c1, c2, c3, c-10086],
    //  'key-2' : [c1, c2],
    //  'key-3' : [c-10086]
    // }

    /* This key is not already watched in this DB. Let's add it */
    // 检查 key 是否存在于数据库的 watched_keys 字典中
    clients = dictFetchValue(c->db->watched_keys,key);
    // 如果不存在的话,添加它
    if (!clients) { 
        // 值为链表
        clients = listCreate();
        // 关联键值对到字典
        dictAdd(c->db->watched_keys,key,clients);
        incrRefCount(key);
    }
    // 将客户端添加到链表的末尾
    listAddNodeTail(clients,c);

    /* Add the new key to the list of keys watched by this client */
    // 将新 watchedKey 结构添加到客户端 watched_keys 链表的表尾
    // 以下是一个添加 watchedKey 结构的例子
    // before:
    // [
    //  {
    //   'key': 'key-1',
    //   'db' : 0
    //  }
    // ]
    // after client watch key-123321 in db 0:
    // [
    //  {
    //   'key': 'key-1',
    //   'db' : 0
    //  }
    //  ,
    //  {
    //   'key': 'key-123321',
    //   'db': 0
    //  }
    // ]
    wk = zmalloc(sizeof(*wk));
    wk->key = key;
    wk->db = c->db;
    incrRefCount(key);
    listAddNodeTail(c->watched_keys,wk);
}
Esempio n. 6
0
File: vm.c Progetto: ambakshi/redis
void vmInit(void) {
    off totsize;
    int pipefds[2];
    size_t stacksize;
#ifndef _WIN32
    struct flock fl;
#endif

    if (server.vm_max_threads != 0)
        zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */

    redisLog(REDIS_NOTICE,"Using '%s' as swap file",server.vm_swap_file);
    /* Try to open the old swap file, otherwise create it */
    if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) {
        server.vm_fp = fopen(server.vm_swap_file,"w+b");
    }

    if (server.vm_fp == NULL) {
        redisLog(REDIS_WARNING,
            "Can't open the swap file: %s. Exiting.",
            strerror(errno));
        exit(1);
    }
    server.vm_fd = fileno(server.vm_fp);
    /* Lock the swap file for writing, this is useful in order to avoid
     * another instance to use the same swap file for a config error. */
#ifdef _WIN32

    // LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0)
    if(!LockFile((HANDLE) _get_osfhandle(server.vm_fd), (0x40000001), 0, 1, 0) ) {
        redisLog(REDIS_WARNING,
            "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno));
        WSACleanup();
        exit(1);
    }
#else
    fl.l_type = F_WRLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start = fl.l_len = 0;
    if (fcntl(server.vm_fd,F_SETLK,&fl) == -1) {
        redisLog(REDIS_WARNING,
            "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno));
        exit(1);
    }
#endif
    /* Initialize */
    server.vm_next_page = 0;
    server.vm_near_pages = 0;
    server.vm_stats_used_pages = 0;
    server.vm_stats_swapped_objects = 0;
    server.vm_stats_swapouts = 0;
    server.vm_stats_swapins = 0;
    totsize = server.vm_pages*server.vm_page_size;
#ifdef _WIN32
    redisLog(REDIS_NOTICE,"Allocating %lld bytes of swap file",(long long) totsize);
#else
    redisLog(REDIS_NOTICE,"Allocating %lld bytes of swap file",totsize);
#endif
    if (ftruncate(server.vm_fd,totsize) == -1) {
        redisLog(REDIS_WARNING,"Can't ftruncate swap file: %s. Exiting.",
            strerror(errno));
        exit(1);
    } else {
        redisLog(REDIS_NOTICE,"Swap file allocated with success");
    }
    server.vm_bitmap = zcalloc((server.vm_pages+7)/8);
    redisLog(REDIS_VERBOSE,"Allocated %lld bytes page table for %lld pages",
        (long long) (server.vm_pages+7)/8, server.vm_pages);

    /* Initialize threaded I/O (used by Virtual Memory) */
    server.io_newjobs = listCreate();
    server.io_processing = listCreate();
    server.io_processed = listCreate();
    server.io_ready_clients = listCreate();
#ifndef _WIN32
    /* moved to InitSharedObjecsts since they are first time used there */
    pthread_mutex_init(&server.io_mutex,NULL);
    pthread_mutex_init(&server.io_swapfile_mutex,NULL);
#endif
    server.io_active_threads = 0;
    if (pipe(pipefds) == -1) {
        redisLog(REDIS_WARNING,"Unable to intialized VM: pipe(2): %s. Exiting."
            ,strerror(errno));
        exit(1);
    }
    server.io_ready_pipe_read = pipefds[0];
    server.io_ready_pipe_write = pipefds[1];
#ifndef _WIN32
    /* Windows distincts pipe and socket destriptors */
    /* We will use blocking pipe, with peek before blocking read */
    redisAssert(anetNonBlock(NULL,server.io_ready_pipe_read) != ANET_ERR);
#endif
    /* LZF requires a lot of stack */
    pthread_attr_init(&server.io_threads_attr);
    pthread_attr_getstacksize(&server.io_threads_attr, &stacksize);

    /* Solaris may report a stacksize of 0, let's set it to 1 otherwise
     * multiplying it by 2 in the while loop later will not really help ;) */
    if (!stacksize) stacksize = 1;

    while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
    pthread_attr_setstacksize(&server.io_threads_attr, stacksize);

    /* Listen for events in the threaded I/O pipe */
#ifdef _WIN32
    /* Windows fix: need to pass flag that notifies Api that file descriptor is pipe */
    if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE | AE_PIPE,
        vmThreadedIOCompletedJob, NULL) == AE_ERR)
        oom("creating file event");
#else
    if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE,
        vmThreadedIOCompletedJob, NULL) == AE_ERR)
        oom("creating file event");
#endif
}
Esempio n. 7
0
Q330_CFG *q330ReadCfg(char *name)
{
int i, status, ntoken;
FILE *fp = NULL;
Q330_CFG *cfg = NULL;
QDP_TYPE_C1_QCAL DefaultCalib = Q330_DEFAULT_CALIB;
char *token[MAX_TOKEN];
char input[MAXLINELEN];
LNKLST *addr = NULL, *detector = NULL;
static char *DefaultName = Q330_DEFAULT_CFG_PATH;

/* Locate and open the file */

    if (name == NULL) name = getenv(Q330_CFG_ENV_STRING);
    if (name == NULL) name = DefaultName;

    if (!utilFileExists(name)) return NULL;

    if ((fp = fopen(name, "r")) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

/* Create the config structure and save the name */

    if ((cfg = CreateCfg(name)) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL);
    cfg->calib = DefaultCalib;

/* Initialize the temp lists */

    if ((addr = listCreate()) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL);
    if ((detector = listCreate()) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

/* Read the contents of the file into the list */

    while ((status = utilGetLine(fp, input, MAXLINELEN, COMMENT, NULL)) == 0) {
        ntoken = utilParse(input, token, " \t", MAX_TOKEN, QUOTE);

    /* "q330" entries define Q330_ADDR structures */

        if (strcasecmp(token[0], "q330") == 0) {

            if (!AddAddr(addr, token, ntoken)) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

    /* "detector" entries define Q330_ADDR structures */

        } else if (strcasecmp(token[0], "detector") == 0) {

            if (!AddDetector(detector, token, ntoken)) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

    /* "calib" entries define QDP_TYPE_C1_QCAL structures */

        } else if (strcasecmp(token[0], "calib") == 0) {

            if (!AddCalib(&cfg->calib, token, ntoken)) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

    /* anything else is not supported */

        } else {
            errno = EINVAL;
            return ReadCfgCleanReturn(fp, cfg, addr, NULL);
        }
    }

    if (status != 1) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

/* Copy the addresses into the output structure */

    if (!listSetArrayView(addr)) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

    cfg->naddr = addr->count;
    if ((cfg->addr = (Q330_ADDR *) malloc(sizeof(Q330_ADDR) * cfg->naddr)) == NULL) return ReadCfgCleanReturn(fp, cfg, addr, NULL);
    for (i = 0; i < addr->count; i++) cfg->addr[i] = *((Q330_ADDR *) addr->array[i]);

/* Copy the detectors into the output structure */

    if (!listSetArrayView(detector)) return ReadCfgCleanReturn(fp, cfg, addr, NULL);

    cfg->ndetector = detector->count;
    if ((cfg->detector = (Q330_DETECTOR *) malloc(sizeof(Q330_DETECTOR) * cfg->ndetector)) == NULL) return ReadCfgCleanReturn(fp, cfg, detector, NULL);
    for (i = 0; i < detector->count; i++) cfg->detector[i] = *((Q330_DETECTOR *) detector->array[i]);

/* All done, return the newly created structure */

    return ReadCfgCleanReturn(fp, NULL, addr, cfg);
}
Esempio n. 8
0
/* *
 * slotscheck
 * */
void
slotscheckCommand(redisClient *c) {
    sds bug = NULL;
    int i;
    for (i = 0; i < HASH_SLOTS_SIZE && bug == NULL; i ++) {
        dict *d = c->db->hash_slots[i];
        if (dictSize(d) == 0) {
            continue;
        }
        list *l = listCreate();
        listSetFreeMethod(l, decrRefCountVoid);
        unsigned long cursor = 0;
        do {
            cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l);
            while (1) {
                listNode *head = listFirst(l);
                if (head == NULL) {
                    break;
                }
                robj *key = listNodeValue(head);
                if (lookupKey(c->db, key) == NULL) {
                    if (bug == NULL) {
                        bug = sdsdup(key->ptr);
                    }
                }
                listDelNode(l, head);
            }
        } while (cursor != 0 && bug == NULL);
        listRelease(l);
    }
    if (bug != NULL) {
        addReplyErrorFormat(c, "step 1, miss = '%s'", bug);
        sdsfree(bug);
        return;
    }
    do {
        dict *d = c->db->dict;
        if (dictSize(d) == 0) {
            break;
        }
        list *l = listCreate();
        listSetFreeMethod(l, decrRefCountVoid);
        unsigned long cursor = 0;
        do {
            cursor = dictScan(d, cursor, slotsScanSdsKeyCallback, l);
            while (1) {
                listNode *head = listFirst(l);
                if (head == NULL) {
                    break;
                }
                robj *key = listNodeValue(head);
                int slot = slots_num(key->ptr, NULL);
                if (dictFind(c->db->hash_slots[slot], key->ptr) == NULL) {
                    if (bug == NULL) {
                        bug = sdsdup(key->ptr);
                    }
                }
                listDelNode(l, head);
            }
        } while (cursor != 0 && bug == NULL);
        listRelease(l);
    } while (0);
    if (bug != NULL) {
        addReplyErrorFormat(c, "step 2, miss = '%s'", bug);
        sdsfree(bug);
        return;
    }
    addReply(c, shared.ok);
}
Esempio n. 9
0
list *createClientlist( ) {
    list *clients = listCreate();
    listSetFreeMethod(clients, listDeleteClientObjects);
    listSetMatchMethod(clients, listMatchClientObjects);
    return clients;
}
Esempio n. 10
0
int main(int argc, char **argv) {
    int i = 0;

    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    config.el = aeCreateEventLoop();
    config.debug = 0;
    config.done = 0;
    config.clients = listCreate();
    config.num_clients = 50;
    config.num_requests = 10000;
    config.issued_requests = 0;

    config.keepalive = 1;
    config.set_perc = 50;
    config.del_perc = 0;
    config.swapin_perc = 0;
    config.lpush_perc = 0;
    config.lpop_perc = 0;
    config.hset_perc = 0;
    config.hget_perc = 0;
    config.hgetall_perc = 0;
    config.datasize_min = 1;
    config.datasize_max = 64;
    config.keyspace = DEFAULT_KEYSPACE; /* 100k */
    config.hashkeyspace = DEFAULT_HASHKEYSPACE; /* 1k */
    config.check = 0;
    config.rand = 0;
    config.longtail = 0;
    config.quiet = 0;
    config.loop = 0;
    config.idlemode = 0;
    config.latency = NULL;
    config.latency = zmalloc(sizeof(int)*(MAX_LATENCY+1));
    config.ctrlc = 0;
    config.prngseed = (unsigned int) (microseconds()^getpid());

    config.hostip = "127.0.0.1";
    config.hostport = 6379;

    parseOptions(argc,argv);
    config.databuf = zmalloc(config.datasize_max);

    if (config.keepalive == 0) {
        printf("WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\n");
    }

    if (config.idlemode) {
        printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.num_clients);
        memset(config.optab,REDIS_IDLE,100);
    } else {
        /* Setup the operation table. Start with a table with just GET
         * operations and overwrite it with others as needed. */
        memset(config.optab,REDIS_GET,100);
        fillOpTab(&i,REDIS_SET,config.set_perc);
        fillOpTab(&i,REDIS_DEL,config.del_perc);
        fillOpTab(&i,REDIS_LPUSH,config.lpush_perc);
        fillOpTab(&i,REDIS_LPOP,config.lpop_perc);
        fillOpTab(&i,REDIS_HSET,config.hset_perc);
        fillOpTab(&i,REDIS_HGET,config.hget_perc);
        fillOpTab(&i,REDIS_HGETALL,config.hgetall_perc);
        fillOpTab(&i,REDIS_SWAPIN,config.swapin_perc);
    }

    signal(SIGINT,ctrlc);
    srandom(config.prngseed);
    printf("PRNG seed is: %u - use the 'seed' option to reproduce the same sequence\n", config.prngseed);
    do {
        prepareForBenchmark();
        createMissingClients();
        aeMain(config.el);
        endBenchmark();
    } while(config.loop);

    return 0;
}
Esempio n. 11
0
/*
 * Entry point of Multi-core Insense runtime.
 */
int main(int argc, char* argv[]) {
	PRINTFMC("Cache line size: %dB\n", cache_line_size());
	PRINTFMC("Main thread: %u\n", (unsigned) pthread_self());

	errval_t err;
	coreid_t mycore = disp_get_core_id();

	if (argc == 2) {
		num_to_span = atoi(argv[1]);
		if(num_to_span==0)
			all_spanned = true;		

		debug_printf("Spanning onto %d cores\n", num_to_span);
		for (int i = 1; i < num_to_span; i++) {
			err = domain_new_dispatcher(mycore + i, span_cb, NULL);
		    
			if (err_is_fail(err)) {
				DEBUG_ERR(err, "failed span %d", i);
			} 
		}
	} else {
		debug_printf("ERROR: Must specify number of cores to span\n");
		return EXIT_FAILURE;
	}

	posixcompat_pthread_set_placement_fn(rrPlacement);

	while (!all_spanned) {
		thread_yield();
	}

	my_mutex_init(&shared_heap_mutex);
#if HEAPS == HEAP_PRIVATE // Private heaps
	// Initialize mutex
	if (pthread_mutex_init(&thread_lock, NULL ) != 0) {
		PRINTF("Mutex initialization failed.\n");
		return -1;
	}
#endif

	mainThread = pthread_self(); // Note the ID of the main thread.

	// Create a list for storing references to p-threads
	threadList = listCreate();

	// Create map used to store memory locations of small heaps (using Thread safe list)
	SHList = listCreate();

	// Create map used to store memory locations what is allocated using malloc
	mallocList = listCreate();

// Start recording execution time
#if TIMING
	// CPU time
	uint64_t start, end;
	uint64_t tsc_per_ms = 0;
	sys_debug_get_tsc_per_ms(&tsc_per_ms);
	start = rdtsc();
#endif

	// Call primordial_main.
	primordial_main(NULL );

	// Join all p-threads
	if (threadList != NULL ) {
		listJoinThreads(threadList);
	}

// Stop recording execution time
#if TIMING
	end = rdtsc();
	
	uint64_t diff = (end - start) / tsc_per_ms;
	float elapsed = (diff / 1000) + ((diff % 1000) / 1000.0);

	printf("CPU:  %f seconds elapsed\n", elapsed);
#endif

	// Destroy lists and free memory
	listDestroy(threadList);
	listDestroy(SHList);
	listDestroy(mallocList);
#if HEAPS == HEAP_PRIVATE
	pthread_mutex_destroy(&thread_lock); 	// Destroy mutex lock used with pthreads
#endif
	return 0;
}
Esempio n. 12
0
static void *check_thread_run(void *args)
{
    int ret;
    thread_data *cdata = args;
    redis_group *srgroup = cdata->srgroup;
    redis_group *trgroup = cdata->trgroup;
    dict *nodes;
    dictEntry *de;
    dictIterator *di;
    redis_node *rnode;
    struct mbuf *mbuf;

    nodes = srgroup->nodes;
    di = dictGetIterator(nodes);
    while ((de = dictNext(di)) != NULL) {
    	rnode = dictGetVal(de);

        rnode->write_data = cdata;

        /* remove the not used part for source redis node */
        if (rnode->rdb != NULL) {
            redis_rdb_deinit(rnode->rdb);
            rmt_free(rnode->rdb);
            rnode->rdb = NULL;
        }
        
        if (rnode->cmd_data != NULL) {
            while (!mttlist_empty(rnode->cmd_data)) {
                mbuf = mttlist_pop(rnode->cmd_data);
                mbuf_put(mbuf);
            }
            
            mttlist_destroy(rnode->cmd_data);
            rnode->cmd_data = NULL;
        }

        if (rnode->sockpairfds[0] > 0) {
            close(rnode->sockpairfds[0]);
            rnode->sockpairfds[0] = -1;
        }

        if (rnode->sockpairfds[1] > 0) {
            close(rnode->sockpairfds[1]);
            rnode->sockpairfds[1] = -1;
        }

        if (rnode->rr != NULL) {
            redis_replication_deinit(rnode->rr);
            rmt_free(rnode->rr);
            rnode->rr = NULL;
        }

        if (rnode->piece_data != NULL) {
            while ((mbuf = listPop(rnode->piece_data)) != NULL) {
                mbuf_put(mbuf);
            }
            
            listRelease(rnode->piece_data);
            rnode->piece_data = NULL;
        }

        /* add the used part for source redis node */
        if (rnode->send_data == NULL) {
            rnode->send_data = listCreate();
            if (rnode->send_data == NULL) {
                log_error("ERROR: Create msg list failed: out of memory");
                return 0;
            }
        }

        if (rnode->sent_data == NULL) {
            rnode->sent_data = listCreate();
            if (rnode->sent_data == NULL) {
                log_error("ERROR: Create msg list failed: out of memory");
                return 0;
            }
        }

        if (rnode->sk_event < 0) {
            rnode->sk_event = socket(AF_INET, SOCK_STREAM, 0);
            if(rnode->sk_event < 0){
                log_error("ERROR: Create sk_event for node[%s] failed: %s", 
                    rnode->addr, strerror(errno));
                return 0;
            }
        }
        ret = aeCreateFileEvent(cdata->loop, rnode->sk_event, 
            AE_WRITABLE, check_begin, rnode);
        if (ret != AE_OK) {
            log_error("ERROR: send_data event create %ld failed: %s",
                cdata->thread_id, strerror(errno));
            return 0;
        }
    }
    dictReleaseIterator(di);

    nodes = trgroup->nodes;
    di = dictGetIterator(nodes);
    while ((de = dictNext(di)) != NULL) {
    	rnode = dictGetVal(de);

        rnode->write_data = cdata;
    }
    dictReleaseIterator(di);

    aeMain(cdata->loop);
    return 0;
}
Esempio n. 13
0
/* This function should be called by Redis every time a single command,
 * a MULTI/EXEC block, or a Lua script, terminated its execution after
 * being called by a client.
 *
 * All the keys with at least one client blocked that received at least
 * one new element via some PUSH/XADD operation are accumulated into
 * the server.ready_keys list. This function will run the list and will
 * serve clients accordingly. Note that the function will iterate again and
 * again as a result of serving BRPOPLPUSH we can have new blocking clients
 * to serve because of the PUSH side of BRPOPLPUSH. */
void handleClientsBlockedOnKeys(void) {
    while(listLength(server.ready_keys) != 0) {
        list *l;

        /* Point server.ready_keys to a fresh list and save the current one
         * locally. This way as we run the old list we are free to call
         * signalKeyAsReady() that may push new elements in server.ready_keys
         * when handling clients blocked into BRPOPLPUSH. */
        l = server.ready_keys;
        server.ready_keys = listCreate();

        while(listLength(l) != 0) {
            listNode *ln = listFirst(l);
            readyList *rl = ln->value;

            /* First of all remove this key from db->ready_keys so that
             * we can safely call signalKeyAsReady() against this key. */
            dictDelete(rl->db->ready_keys,rl->key);

            /* Serve clients blocked on list key. */
            robj *o = lookupKeyWrite(rl->db,rl->key);
            if (o != NULL && o->type == OBJ_LIST) {
                dictEntry *de;

                /* We serve clients in the same order they blocked for
                 * this key, from the first blocked to the last. */
                de = dictFind(rl->db->blocking_keys,rl->key);
                if (de) {
                    list *clients = dictGetVal(de);
                    int numclients = listLength(clients);

                    while(numclients--) {
                        listNode *clientnode = listFirst(clients);
                        client *receiver = clientnode->value;

                        if (receiver->btype != BLOCKED_LIST) {
                            /* Put on the tail, so that at the next call
                             * we'll not run into it again. */
                            listDelNode(clients,clientnode);
                            listAddNodeTail(clients,receiver);
                            continue;
                        }

                        robj *dstkey = receiver->bpop.target;
                        int where = (receiver->lastcmd &&
                                     receiver->lastcmd->proc == blpopCommand) ?
                                    LIST_HEAD : LIST_TAIL;
                        robj *value = listTypePop(o,where);

                        if (value) {
                            /* Protect receiver->bpop.target, that will be
                             * freed by the next unblockClient()
                             * call. */
                            if (dstkey) incrRefCount(dstkey);
                            unblockClient(receiver);

                            if (serveClientBlockedOnList(receiver,
                                rl->key,dstkey,rl->db,value,
                                where) == C_ERR)
                            {
                                /* If we failed serving the client we need
                                 * to also undo the POP operation. */
                                    listTypePush(o,value,where);
                            }

                            if (dstkey) decrRefCount(dstkey);
                            decrRefCount(value);
                        } else {
                            break;
                        }
                    }
                }

                if (listTypeLength(o) == 0) {
                    dbDelete(rl->db,rl->key);
                    notifyKeyspaceEvent(NOTIFY_GENERIC,"del",rl->key,rl->db->id);
                }
                /* We don't call signalModifiedKey() as it was already called
                 * when an element was pushed on the list. */
            }

            /* Serve clients blocked on stream key. */
            else if (o != NULL && o->type == OBJ_STREAM) {
                dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
                stream *s = o->ptr;

                /* We need to provide the new data arrived on the stream
                 * to all the clients that are waiting for an offset smaller
                 * than the current top item. */
                if (de) {
                    list *clients = dictGetVal(de);
                    listNode *ln;
                    listIter li;
                    listRewind(clients,&li);

                    while((ln = listNext(&li))) {
                        client *receiver = listNodeValue(ln);
                        if (receiver->btype != BLOCKED_STREAM) continue;
                        streamID *gt = dictFetchValue(receiver->bpop.keys,
                                                      rl->key);
                        if (s->last_id.ms > gt->ms ||
                            (s->last_id.ms == gt->ms &&
                             s->last_id.seq > gt->seq))
                        {
                            streamID start = *gt;
                            start.seq++; /* Can't overflow, it's an uint64_t */

                            /* If we blocked in the context of a consumer
                             * group, we need to resolve the group and
                             * consumer here. */
                            streamCG *group = NULL;
                            streamConsumer *consumer = NULL;
                            if (receiver->bpop.xread_group) {
                                group = streamLookupCG(s,
                                        receiver->bpop.xread_group->ptr);
                                /* In theory if the group is not found we
                                 * just perform the read without the group,
                                 * but actually when the group, or the key
                                 * itself is deleted (triggering the removal
                                 * of the group), we check for blocked clients
                                 * and send them an error. */
                            }
                            if (group) {
                                consumer = streamLookupConsumer(group,
                                           receiver->bpop.xread_consumer->ptr,
                                           1);
                            }

                            /* Note that after we unblock the client, 'gt'
                             * and other receiver->bpop stuff are no longer
                             * valid, so we must do the setup above before
                             * this call. */
                            unblockClient(receiver);

                            /* Emit the two elements sub-array consisting of
                             * the name of the stream and the data we
                             * extracted from it. Wrapped in a single-item
                             * array, since we have just one key. */
                            addReplyMultiBulkLen(receiver,1);
                            addReplyMultiBulkLen(receiver,2);
                            addReplyBulk(receiver,rl->key);

                            streamPropInfo pi = {
                                rl->key,
                                receiver->bpop.xread_group
                            };
                            streamReplyWithRange(receiver,s,&start,NULL,
                                                 receiver->bpop.xread_count,
                                                 0, group, consumer, 0, &pi);
                        }
                    }
                }
            }

            /* Free this item. */
            decrRefCount(rl->key);
            zfree(rl);
            listDelNode(l,ln);
        }
        listRelease(l); /* We have the new list on place at this point. */
    }
}
Esempio n. 14
0
void
init_res (void)
{
  static char fname[] = "init_res";
  int i, maxfds;

  if (logclass & (LC_TRACE | LC_HANG))
    ls_syslog (LOG_DEBUG, "%s: Entering this routine...", fname);



  if (!sbdMode)
    {
      if (!debug)
	{

	  if (geteuid () || getuid ())
	    {
	      fprintf (stderr, "RES should be run as root.\n");
	      fflush (stderr);
	      resExit_ (1);
	    }



	  chdir ("/tmp");
	}



      if (debug <= 1)
	{

	  daemonize_ ();



	  ls_openlog ("res", resParams[LSF_LOGDIR].paramValue, 0,
		      resParams[LSF_LOG_MASK].paramValue);



	  umask (0);





	  nice (NICE_LEAST);
	}
    }

  if ((Myhost = ls_getmyhostname ()) == NULL)
    {
      ls_syslog (LOG_ERR, I18N_FUNC_FAIL_MM, fname, "ls_getmyhostname");
      resExit_ (-1);
    }


  if (isatty (0))
    {
      tcgetattr (0, &defaultTty.attr);

      defaultTty.ws.ws_row = 24;
      defaultTty.ws.ws_col = 80;
      defaultTty.ws.ws_xpixel = defaultTty.ws.ws_ypixel = 0;
    }
  else
    {
      defaultTty.ws.ws_row = 24;
      defaultTty.ws.ws_col = 80;
      defaultTty.ws.ws_xpixel = defaultTty.ws.ws_ypixel = 0;
    }

  if (!sbdMode)
    {

      init_AcceptSock ();
    }

  client_cnt = child_cnt = 0;

  for (i = 0; i < MAXCLIENTS_HIGHWATER_MARK + 1; i++)
    {
      clients[i] = NULL;
    }

  children =
    (struct child **) calloc (sysconf (_SC_OPEN_MAX),
			      sizeof (struct children *));
  if (!children)
    {
      ls_syslog (LOG_ERR, I18N_FUNC_FAIL_M, fname, "calloc");
      resExit_ (-1);
    }

  maxfds = sysconf (_SC_OPEN_MAX);

  for (i = 0; i < maxfds; i++)
    {
      children[i] = NULL;
    }

  initConn2NIOS ();
  resNotifyList = listCreate ("resNotifyList");
  if (!resNotifyList)
    {
      ls_syslog (LOG_ERR, I18N_FUNC_FAIL_M, fname, "listCreate");
      resExit_ (-1);
    }




  ls_syslog (LOG_INFO, (_i18n_msg_get (ls_catd, NL_SETN, 5346, "Daemon started")));	/* catgets 5346 */

  initResLog ();

}
Esempio n. 15
0
rstatus_t
init_server(struct instance *nci)
{
    rstatus_t status;
    uint32_t i;
    redisDb *db;

    vr_conf *conf;

    conf = conf_create(nci->conf_filename);

    server.pid = getpid();
    server.configfile = getAbsolutePath(nci->conf_filename);
    server.hz = 10;
    server.dbnum = 16;
    array_init(&server.dbs, server.dbnum, sizeof(redisDb));
    server.pidfile = nci->pid_filename;
    server.executable = NULL;
    server.activerehashing = CONFIG_DEFAULT_ACTIVE_REHASHING;
    get_random_hex_chars(server.runid, CONFIG_RUN_ID_SIZE);
    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
    server.requirepass = NULL;

    server.starttime = time(NULL);

    server.maxclients = conf->max_clients;
    server.maxmemory = conf->maxmemory == CONF_UNSET_NUM ? 0 : conf->maxmemory;
    server.maxmemory_policy = CONFIG_DEFAULT_MAXMEMORY_POLICY;

    server.client_max_querybuf_len = PROTO_MAX_QUERYBUF_LEN;

    server.commands = dictCreate(&commandTableDictType,NULL);
    populateCommandTable();
    server.delCommand = lookupCommandByCString("del");
    server.multiCommand = lookupCommandByCString("multi");
    server.lpushCommand = lookupCommandByCString("lpush");
    server.lpopCommand = lookupCommandByCString("lpop");
    server.rpopCommand = lookupCommandByCString("rpop");
    server.sremCommand = lookupCommandByCString("srem");
    server.execCommand = lookupCommandByCString("exec");

    for (i = 0; i < server.dbnum; i ++) {
        db = array_push(&server.dbs);
        redisDbInit(db);
    }
    
    server.monitors = listCreate();

    server.loading = 0;

    server.lua_timedout = 0;

    server.aof_state = AOF_OFF;

    server.stop_writes_on_bgsave_err = 0;

    server.ready_keys = listCreate();

    server.slowlog = listCreate();
    server.slowlog_entry_id = 0;
    server.slowlog_log_slower_than = -1;
    server.slowlog_max_len = CONFIG_DEFAULT_SLOWLOG_MAX_LEN;

    server.stat_peak_memory = 0;

    server.system_memory_size = zmalloc_get_memory_size();

    server.rdb_child_pid = -1;
    server.aof_child_pid = -1;

    server.hash_max_ziplist_entries = OBJ_HASH_MAX_ZIPLIST_ENTRIES;
    server.hash_max_ziplist_value = OBJ_HASH_MAX_ZIPLIST_VALUE;
    server.list_max_ziplist_size = OBJ_LIST_MAX_ZIPLIST_SIZE;
    server.list_compress_depth = OBJ_LIST_COMPRESS_DEPTH;
    server.set_max_intset_entries = OBJ_SET_MAX_INTSET_ENTRIES;
    server.zset_max_ziplist_entries = OBJ_ZSET_MAX_ZIPLIST_ENTRIES;
    server.zset_max_ziplist_value = OBJ_ZSET_MAX_ZIPLIST_VALUE;
    server.hll_sparse_max_bytes = CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES;

    vr_replication_init();
    
    createSharedObjects();

    status = master_init(conf);
    if (status != VR_OK) {
        log_error("init master thread failed");
        return VR_ERROR;
    }

    server.port = master.listen->port;
    
    status = workers_init(nci->thread_num);
    if (status != VR_OK) {
        log_error("init worker threads failed");
        return VR_ERROR;
    }

    log_debug(LOG_NOTICE, "mem_alloc_lock_type: %s", malloc_lock_type());
    log_debug(LOG_NOTICE, "malloc lib: %s", VR_MALLOC_LIB);

    return VR_OK;
}
Esempio n. 16
0
/* This function should be called by Redis every time a single command,
 * a MULTI/EXEC block, or a Lua script, terminated its execution after
 * being called by a client.
 *
 * All the keys with at least one client blocked that received at least
 * one new element via some PUSH operation are accumulated into
 * the server.ready_keys list. This function will run the list and will
 * serve clients accordingly. Note that the function will iterate again and
 * again as a result of serving BRPOPLPUSH we can have new blocking clients
 * to serve because of the PUSH side of BRPOPLPUSH. */
void handleClientsBlockedOnLists(void) {
    while(listLength(server.ready_keys) != 0) {
        list *l;

        /* Point server.ready_keys to a fresh list and save the current one
         * locally. This way as we run the old list we are free to call
         * signalListAsReady() that may push new elements in server.ready_keys
         * when handling clients blocked into BRPOPLPUSH. */
        l = server.ready_keys;
        server.ready_keys = listCreate();

        while(listLength(l) != 0) {
            listNode *ln = listFirst(l);
            readyList *rl = ln->value;

            /* First of all remove this key from db->ready_keys so that
             * we can safely call signalListAsReady() against this key. */
            dictDelete(rl->db->ready_keys,rl->key);

            /* If the key exists and it's a list, serve blocked clients
             * with data. */
            robj *o = lookupKeyWrite(rl->db,rl->key);
            if (o != NULL && o->type == REDIS_LIST) {
                dictEntry *de;

                /* We serve clients in the same order they blocked for
                 * this key, from the first blocked to the last. */
                de = dictFind(rl->db->blocking_keys,rl->key);
                if (de) {
                    list *clients = dictGetVal(de);
                    int numclients = listLength(clients);

                    while(numclients--) {
                        listNode *clientnode = listFirst(clients);
                        redisClient *receiver = clientnode->value;
                        robj *dstkey = receiver->bpop.target;
                        int where = (receiver->lastcmd &&
                                     receiver->lastcmd->proc == blpopCommand) ?
                                    REDIS_HEAD : REDIS_TAIL;
                        robj *value = listTypePop(o,where);

                        if (value) {
                            /* Protect receiver->bpop.target, that will be
                             * freed by the next unblockClientWaitingData()
                             * call. */
                            if (dstkey) incrRefCount(dstkey);
                            unblockClientWaitingData(receiver);

                            if (serveClientBlockedOnList(receiver,
                                rl->key,dstkey,rl->db,value,
                                where) == REDIS_ERR)
                            {
                                /* If we failed serving the client we need
                                 * to also undo the POP operation. */
                                    listTypePush(o,value,where);
                            }

                            if (dstkey) decrRefCount(dstkey);
                            decrRefCount(value);
                        } else {
                            break;
                        }
                    }
                }
                
                if (listTypeLength(o) == 0) dbDelete(rl->db,rl->key);
                /* We don't call signalModifiedKey() as it was already called
                 * when an element was pushed on the list. */
            }

            /* Free this item. */
            decrRefCount(rl->key);
            zfree(rl);
            listDelNode(l,ln);
        }
        listRelease(l); /* We have the new list on place at this point. */
    }
}
Esempio n. 17
0
static list *index_list_create() {
    list *idx = listCreate();
    listSetMatchMethod(idx, fts_doc_match);
    listSetFreeMethod(idx, index_item_free);
    return idx;
}
Esempio n. 18
0
/* *
 * do migrate mutli key-value(s) for {slotsmgrt/slotsmgrtone}with tag commands
 * return value:
 *    -1 - error happens
 *   >=0 - # of success migration
 * */
static int
slotsmgrttag_command(redisClient *c, sds host, sds port, int timeout, robj *key) {
    int taglen;
    void *tag = slots_tag(key->ptr, &taglen);
    if (tag == NULL) {
        return slotsmgrtone_command(c, host, port, timeout, key);
    }

    int fd = slotsmgrt_get_socket(c, host, port, timeout);
    if (fd == -1) {
        return -1;
    }

    list *l = listCreate();
    listSetFreeMethod(l, decrRefCountVoid);
    do {
        uint32_t crc;
        int slot = slots_num(key->ptr, &crc);
        dict *d = c->db->hash_slots[slot];
        long long cursor = 0;
        void *args[] = {l, tag, &taglen, (void *)(long)crc};
        do {
            cursor = dictScan(d, cursor, slotsScanSdsKeyTagCallback, args);
        } while (cursor != 0);
    } while (0);

    int max = listLength(l);
    if (max == 0) {
        listRelease(l);
        return 0;
    }

    robj **keys = zmalloc(sizeof(robj *) * max);
    robj **vals = zmalloc(sizeof(robj *) * max);

    int n = 0;
    for (int i = 0; i < max; i ++) {
        listNode *head = listFirst(l);
        robj *key = listNodeValue(head);
        robj *val = lookupKeyWrite(c->db, key);
        if (val != NULL) {
            keys[n] = key;
            vals[n] = val;
            n ++;
            incrRefCount(key);
        }
        listDelNode(l, head);
    }

    int ret = 0;
    if (n != 0) {
        if (slotsmgrt(c, host, port, fd, c->db->id, timeout, keys, vals, n) != 0) {
            slotsmgrt_close_socket(host, port);
            ret = -1;
        } else {
            slotsremove(c, keys, n, 1);
            ret = n;
        }
    }

    listRelease(l);
    for (int i = 0; i < n; i ++) {
        decrRefCount(keys[i]);
    }
    zfree(keys);
    zfree(vals);
    return ret;
}
Esempio n. 19
0
//函数会在redis每次执行完单个命令,事务块或lua脚本之后被调用
//对于所有被阻塞在client的key来说,只要key被执行了PUSH,那么这个key会被加入到server.ready_keys中
//处理client的阻塞状态
void handleClientsBlockedOnLists(void) {
    //只要server.ready_keys还有要解除阻塞的key,就循环遍历server.ready_keys链表
    while(listLength(server.ready_keys) != 0) {
        list *l;

        /* Point server.ready_keys to a fresh list and save the current one
         * locally. This way as we run the old list we are free to call
         * signalListAsReady() that may push new elements in server.ready_keys
         * when handling clients blocked into BRPOPLPUSH. */
        //备份一个server.ready_keys链表
        l = server.ready_keys;
        //生成一个新的空链表
        server.ready_keys = listCreate();

        //只要链表中还有就绪的key
        while(listLength(l) != 0) {
            listNode *ln = listFirst(l);    //链表头结点地址
            readyList *rl = ln->value;      //保存链表节点的值,每个值都是readyList结构

            /* First of all remove this key from db->ready_keys so that
             * we can safely call signalListAsReady() against this key. */
            //从rl->db->ready_keys中删除就绪的key
            dictDelete(rl->db->ready_keys,rl->key);

            /* If the key exists and it's a list, serve blocked clients
             * with data. */
            //以读操作将就绪key的值读出来
            robj *o = lookupKeyWrite(rl->db,rl->key);
            //读出的value对象必须是列表类型
            if (o != NULL && o->type == OBJ_LIST) {
                dictEntry *de;

                /* We serve clients in the same order they blocked for
                 * this key, from the first blocked to the last. */
                // blocking_keys是一个字典,字典的键是造成client阻塞的键,字典的值是链表,保存被阻塞的client
                // 根据key取出被阻塞的client
                de = dictFind(rl->db->blocking_keys,rl->key);
                // 链表非空
                if (de) {
                    // 获取de节点的值
                    list *clients = dictGetVal(de);
                    // 获取链表的长度
                    int numclients = listLength(clients);

                    //遍历链表的所有节点
                    while(numclients--) {
                        // 第一个client节点地址
                        listNode *clientnode = listFirst(clients);
                        // 取出节点的值,是一个client类型
                        client *receiver = clientnode->value;
                        // 从client类型中的target获得要PUSH出的dstkey,该键保存在target中
                        robj *dstkey = receiver->bpop.target;
                        // 获取弹出的位置,根据BRPOPLPUSH命令
                        int where = (receiver->lastcmd &&
                                     receiver->lastcmd->proc == blpopCommand) ?
                                    LIST_HEAD : LIST_TAIL;
                        // 从列表中弹出元素
                        robj *value = listTypePop(o,where);

                        // 弹出成功
                        if (value) {
                            /* Protect receiver->bpop.target, that will be
                             * freed by the next unblockClient()
                             * call. */
                            //增加dstkey的引用计数,保护该键,在unblockClient函数中释放
                            if (dstkey) incrRefCount(dstkey);
                            //取消client的阻塞状态
                            unblockClient(receiver);

                            // 将value推入造成client阻塞的键上,
                            if (serveClientBlockedOnList(receiver,
                                rl->key,dstkey,rl->db,value,
                                where) == C_ERR)
                            {
                                /* If we failed serving the client we need
                                 * to also undo the POP operation. */
                                    // 如果推入失败,则需要键弹出的value还原回去
                                    listTypePush(o,value,where);
                            }
                            // 释放dstkey和value
                            if (dstkey) decrRefCount(dstkey);
                            decrRefCount(value);
                        } else {
                            break;
                        }
                    }
                }

                // 如果弹出了所有元素,将key从数据库中删除
                if (listTypeLength(o) == 0) {
                    dbDelete(rl->db,rl->key);
                }
                /* We don't call signalModifiedKey() as it was already called
                 * when an element was pushed on the list. */
            }

            /* Free this item. */
            //释放所有空间
            decrRefCount(rl->key);
            zfree(rl);
            listDelNode(l,ln);
        }
        //释放原来的ready_keys,因为之前创建了新的链表
        listRelease(l); /* We have the new list on place at this point. */
    }
}
Esempio n. 20
0
File: vm.c Progetto: ambakshi/redis
/* This function makes the clinet 'c' waiting for the key 'key' to be loaded.
 * If there is not already a job loading the key, it is craeted.
 * The key is added to the io_keys list in the client structure, and also
 * in the hash table mapping swapped keys to waiting clients, that is,
 * server.io_waited_keys. */
int waitForSwappedKey(redisClient *c, robj *key) {
    struct dictEntry *de;
    robj *o;
    list *l;

    /* If the key does not exist or is already in RAM we don't need to
     * block the client at all. */
    de = dictFind(c->db->dict,key->ptr);
    if (de == NULL) return 0;
    o = dictGetEntryVal(de);
    if (o->storage == REDIS_VM_MEMORY) {
        return 0;
    } else if (o->storage == REDIS_VM_SWAPPING) {
        /* We were swapping the key, undo it! */
        vmCancelThreadedIOJob(o);
        return 0;
    }

    /* OK: the key is either swapped, or being loaded just now. */

    /* Add the key to the list of keys this client is waiting for.
     * This maps clients to keys they are waiting for. */
    listAddNodeTail(c->io_keys,key);
    incrRefCount(key);

    /* Add the client to the swapped keys => clients waiting map. */
    de = dictFind(c->db->io_keys,key);
    if (de == NULL) {
        int retval;

        /* For every key we take a list of clients blocked for it */
        l = listCreate();
        retval = dictAdd(c->db->io_keys,key,l);
        incrRefCount(key);
        redisAssert(retval == DICT_OK);
    } else {
        l = dictGetEntryVal(de);
    }
    listAddNodeTail(l,c);

    /* Are we already loading the key from disk? If not create a job */
    if (o->storage == REDIS_VM_SWAPPED) {
        iojob *j;
        vmpointer *vp = (vmpointer*)o;

        o->storage = REDIS_VM_LOADING;
        j = zmalloc(sizeof(*j));
        j->type = REDIS_IOJOB_LOAD;
        j->db = c->db;
        j->id = (robj*)vp;
        j->key = key;
        incrRefCount(key);
        j->page = vp->page;
        j->val = NULL;
        j->canceled = 0;
        j->thread = (pthread_t) -1;

        lockThreadedIO();
        queueIOJob(j);
        unlockThreadedIO();
    }
    return 1;
}
Esempio n. 21
0
File: vm.c Progetto: Elbandi/redis
void vmInit(void) {
    off_t totsize;
    int pipefds[2];
    size_t stacksize;
    struct flock fl;

    if (server.vm_max_threads != 0)
        zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */

    redisLog(REDIS_NOTICE,"Using '%s' as swap file",server.vm_swap_file);
    /* Try to open the old swap file, otherwise create it */
    if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) {
        server.vm_fp = fopen(server.vm_swap_file,"w+b");
    }
    if (server.vm_fp == NULL) {
        redisLog(REDIS_WARNING,
            "Can't open the swap file: %s. Exiting.",
            strerror(errno));
        exit(1);
    }
    server.vm_fd = fileno(server.vm_fp);
    /* Lock the swap file for writing, this is useful in order to avoid
     * another instance to use the same swap file for a config error. */
    fl.l_type = F_WRLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start = fl.l_len = 0;
    if (fcntl(server.vm_fd,F_SETLK,&fl) == -1) {
        redisLog(REDIS_WARNING,
            "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno));
        exit(1);
    }
    /* Initialize */
    server.vm_next_page = 0;
    server.vm_near_pages = 0;
    server.vm_stats_used_pages = 0;
    server.vm_stats_swapped_objects = 0;
    server.vm_stats_swapouts = 0;
    server.vm_stats_swapins = 0;
    totsize = server.vm_pages*server.vm_page_size;
    redisLog(REDIS_NOTICE,"Allocating %lld bytes of swap file",totsize);
    if (ftruncate(server.vm_fd,totsize) == -1) {
        redisLog(REDIS_WARNING,"Can't ftruncate swap file: %s. Exiting.",
            strerror(errno));
        exit(1);
    } else {
        redisLog(REDIS_NOTICE,"Swap file allocated with success");
    }
    server.vm_bitmap = zcalloc((server.vm_pages+7)/8);
    redisLog(REDIS_VERBOSE,"Allocated %lld bytes page table for %lld pages",
        (long long) (server.vm_pages+7)/8, server.vm_pages);

    /* Initialize threaded I/O (used by Virtual Memory) */
    server.io_newjobs = listCreate();
    server.io_processing = listCreate();
    server.io_processed = listCreate();
    server.io_ready_clients = listCreate();
    pthread_mutex_init(&server.io_mutex,NULL);
    pthread_mutex_init(&server.io_swapfile_mutex,NULL);
    server.io_active_threads = 0;
    if (pipe(pipefds) == -1) {
        redisLog(REDIS_WARNING,"Unable to intialized VM: pipe(2): %s. Exiting."
            ,strerror(errno));
        exit(1);
    }
    server.io_ready_pipe_read = pipefds[0];
    server.io_ready_pipe_write = pipefds[1];
    redisAssert(anetNonBlock(NULL,server.io_ready_pipe_read) != ANET_ERR);
    /* LZF requires a lot of stack */
    pthread_attr_init(&server.io_threads_attr);
    pthread_attr_getstacksize(&server.io_threads_attr, &stacksize);

    /* Solaris may report a stacksize of 0, let's set it to 1 otherwise
     * multiplying it by 2 in the while loop later will not really help ;) */
    if (!stacksize) stacksize = 1;

    while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
    pthread_attr_setstacksize(&server.io_threads_attr, stacksize);
    /* Listen for events in the threaded I/O pipe */
    if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE,
        vmThreadedIOCompletedJob, NULL) == AE_ERR)
        oom("creating file event");
}
Esempio n. 22
0
int main(int argc, const char **argv) {
    int i;
    char *data, *cmd;
    int len;

    client c;

#ifdef _WIN32
    w32initWinSock();
#endif

    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    config.numclients = 50;
    config.requests = 10000;
    config.liveclients = 0;
    config.el = aeCreateEventLoop();
    aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);
    config.keepalive = 1;
    config.datasize = 3;
    config.randomkeys = 0;
    config.randomkeys_keyspacelen = 0;
    config.quiet = 0;
    config.loop = 0;
    config.idlemode = 0;
    config.latency = NULL;
    config.clients = listCreate();
    config.hostip = "127.0.0.1";
    config.hostport = 6379;
    config.hostsocket = NULL;

    i = parseOptions(argc,argv);
    argc -= i;
    argv += i;

    config.latency = zmalloc(sizeof(long long)*config.requests);

    if (config.keepalive == 0) {
        printf("WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\n");
    }

    if (config.idlemode) {
        printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.numclients);
        c = createClient("",0); /* will never receive a reply */
        createMissingClients(c);
        aeMain(config.el);
        /* and will wait for every */
    }

    /* Run benchmark with command in the remainder of the arguments. */
    if (argc) {
        sds title = sdsnew(argv[0]);
        for (i = 1; i < argc; i++) {
            title = sdscatlen(title, " ", 1);
            title = sdscatlen(title, (char*)argv[i], strlen(argv[i]));
        }

        do {
            len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
            benchmark(title,cmd,len);
            free(cmd);
        } while(config.loop);

        return 0;
    }

    /* Run default benchmark suite. */
    do {
        data = zmalloc(config.datasize+1);
        memset(data,'x',config.datasize);
        data[config.datasize] = '\0';

        benchmark("PING (inline)","PING\r\n",6);

        len = redisFormatCommand(&cmd,"PING");
        benchmark("PING",cmd,len);
        free(cmd);

        const char *argv[21];
        argv[0] = "MSET";
        for (i = 1; i < 21; i += 2) {
            argv[i] = "foo:rand:000000000000";
            argv[i+1] = data;
        }
        len = redisFormatCommandArgv(&cmd,21,argv,NULL);
        benchmark("MSET (10 keys)",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"SET foo:rand:000000000000 %s",data);
        benchmark("SET",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"GET foo:rand:000000000000");
        benchmark("GET",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"INCR counter:rand:000000000000");
        benchmark("INCR",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LPUSH mylist %s",data);
        benchmark("LPUSH",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LPOP mylist");
        benchmark("LPOP",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"SADD myset counter:rand:000000000000");
        benchmark("SADD",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"SPOP myset");
        benchmark("SPOP",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LPUSH mylist %s",data);
        benchmark("LPUSH (again, in order to bench LRANGE)",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LRANGE mylist 0 99");
        benchmark("LRANGE (first 100 elements)",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LRANGE mylist 0 299");
        benchmark("LRANGE (first 300 elements)",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LRANGE mylist 0 449");
        benchmark("LRANGE (first 450 elements)",cmd,len);
        free(cmd);

        len = redisFormatCommand(&cmd,"LRANGE mylist 0 599");
        benchmark("LRANGE (first 600 elements)",cmd,len);
        free(cmd);

        printf("\n");
    } while(config.loop);

#ifdef _WIN32
    WSACleanup();
#endif
    return 0;
}
Esempio n. 23
0
/* The SORT command is the most complex command in Redis. Warning: this code
 * is optimized for speed and a bit less for readability */
void sortCommand(redisClient *c) {
    list *operations;
    unsigned int outputlen = 0;
    int desc = 0, alpha = 0;
    int limit_start = 0, limit_count = -1, start, end;
    int j, dontsort = 0, vectorlen;
    int getop = 0; /* GET operation counter */
    robj *sortval, *sortby = NULL, *storekey = NULL;
    redisSortObject *vector; /* Resulting vector to sort */

    /* Lookup the key to sort. It must be of the right types */
    sortval = lookupKeyRead(c->db,c->argv[1]);
    if (sortval == NULL) {
        addReply(c,shared.emptymultibulk);
        return;
    }
    if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST &&
        sortval->type != REDIS_ZSET)
    {
        addReply(c,shared.wrongtypeerr);
        return;
    }

    /* Create a list of operations to perform for every sorted element.
     * Operations can be GET/DEL/INCR/DECR */
    operations = listCreate();
    listSetFreeMethod(operations,zfree);
    j = 2;

    /* Now we need to protect sortval incrementing its count, in the future
     * SORT may have options able to overwrite/delete keys during the sorting
     * and the sorted key itself may get destroied */
    incrRefCount(sortval);

    /* The SORT command has an SQL-alike syntax, parse it */
    while(j < c->argc) {
        int leftargs = c->argc-j-1;
        if (!strcasecmp(c->argv[j]->ptr,"asc")) {
            desc = 0;
        } else if (!strcasecmp(c->argv[j]->ptr,"desc")) {
            desc = 1;
        } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) {
            alpha = 1;
        } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) {
            limit_start = atoi(c->argv[j+1]->ptr);
            limit_count = atoi(c->argv[j+2]->ptr);
            j+=2;
        } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) {
            storekey = c->argv[j+1];
            j++;
        } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) {
            sortby = c->argv[j+1];
            /* If the BY pattern does not contain '*', i.e. it is constant,
             * we don't need to sort nor to lookup the weight keys. */
            if (strchr(c->argv[j+1]->ptr,'*') == NULL) dontsort = 1;
            j++;
        } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) {
            listAddNodeTail(operations,createSortOperation(
                REDIS_SORT_GET,c->argv[j+1]));
            getop++;
            j++;
        } else {
            decrRefCount(sortval);
            listRelease(operations);
            addReply(c,shared.syntaxerr);
            return;
        }
        j++;
    }

    /* Destructively convert encoded sorted sets for SORT. */
    if (sortval->type == REDIS_ZSET) zsetConvert(sortval, REDIS_ENCODING_SKIPLIST);

    /* Load the sorting vector with all the objects to sort */
    switch(sortval->type) {
    case REDIS_LIST: vectorlen = listTypeLength(sortval); break;
    case REDIS_SET: vectorlen =  setTypeSize(sortval); break;
    case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
    default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */
    }
    vector = zmalloc(sizeof(redisSortObject)*vectorlen);
    j = 0;

    if (sortval->type == REDIS_LIST) {
        listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL);
        listTypeEntry entry;
        while(listTypeNext(li,&entry)) {
            vector[j].obj = listTypeGet(&entry);
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
        }
        listTypeReleaseIterator(li);
    } else if (sortval->type == REDIS_SET) {
        setTypeIterator *si = setTypeInitIterator(sortval);
        robj *ele;
        while((ele = setTypeNextObject(si)) != NULL) {
            vector[j].obj = ele;
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
        }
        setTypeReleaseIterator(si);
    } else if (sortval->type == REDIS_ZSET) {
        dict *set = ((zset*)sortval->ptr)->dict;
        dictIterator *di;
        dictEntry *setele;
        di = dictGetIterator(set);
        while((setele = dictNext(di)) != NULL) {
            vector[j].obj = dictGetEntryKey(setele);
            vector[j].u.score = 0;
            vector[j].u.cmpobj = NULL;
            j++;
        }
        dictReleaseIterator(di);
    } else {
        redisPanic("Unknown type");
    }
    redisAssert(j == vectorlen);

    /* Now it's time to load the right scores in the sorting vector */
    if (dontsort == 0) {
        for (j = 0; j < vectorlen; j++) {
            robj *byval;
            if (sortby) {
                /* lookup value to sort by */
                byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
                if (!byval) continue;
            } else {
                /* use object itself to sort by */
                byval = vector[j].obj;
            }

            if (alpha) {
                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);
            } else {
                if (byval->encoding == REDIS_ENCODING_RAW) {
                    vector[j].u.score = strtod(byval->ptr,NULL);
                } else if (byval->encoding == REDIS_ENCODING_INT) {
                    /* Don't need to decode the object if it's
                     * integer-encoded (the only encoding supported) so
                     * far. We can just cast it */
                    vector[j].u.score = (long)byval->ptr;
                } else {
                    redisAssert(1 != 1);
                }
            }

            /* when the object was retrieved using lookupKeyByPattern,
             * its refcount needs to be decreased. */
            if (sortby) {
                decrRefCount(byval);
            }
        }
    }

    /* We are ready to sort the vector... perform a bit of sanity check
     * on the LIMIT option too. We'll use a partial version of quicksort. */
    start = (limit_start < 0) ? 0 : limit_start;
    end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
    if (start >= vectorlen) {
        start = vectorlen-1;
        end = vectorlen-2;
    }
    if (end >= vectorlen) end = vectorlen-1;

    if (dontsort == 0) {
        server.sort_desc = desc;
        server.sort_alpha = alpha;
        server.sort_bypattern = sortby ? 1 : 0;
        if (sortby && (start != 0 || end != vectorlen-1))
            pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end);
        else
            qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare);
    }

    /* Send command output to the output buffer, performing the specified
     * GET/DEL/INCR/DECR operations if any. */
    outputlen = getop ? getop*(end-start+1) : end-start+1;
    if (storekey == NULL) {
        /* STORE option not specified, sent the sorting result to client */
        addReplyMultiBulkLen(c,outputlen);
        for (j = start; j <= end; j++) {
            listNode *ln;
            listIter li;

            if (!getop) addReplyBulk(c,vector[j].obj);
            listRewind(operations,&li);
            while((ln = listNext(&li))) {
                redisSortOperation *sop = ln->value;
                robj *val = lookupKeyByPattern(c->db,sop->pattern,
                    vector[j].obj);

                if (sop->type == REDIS_SORT_GET) {
                    if (!val) {
                        addReply(c,shared.nullbulk);
                    } else {
                        addReplyBulk(c,val);
                        decrRefCount(val);
                    }
                } else {
                    redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
                }
            }
        }
    } else {
        robj *sobj = createZiplistObject();

        /* STORE option specified, set the sorting result as a List object */
        for (j = start; j <= end; j++) {
            listNode *ln;
            listIter li;

            if (!getop) {
                listTypePush(sobj,vector[j].obj,REDIS_TAIL);
            } else {
                listRewind(operations,&li);
                while((ln = listNext(&li))) {
                    redisSortOperation *sop = ln->value;
                    robj *val = lookupKeyByPattern(c->db,sop->pattern,
                        vector[j].obj);

                    if (sop->type == REDIS_SORT_GET) {
                        if (!val) val = createStringObject("",0);

                        /* listTypePush does an incrRefCount, so we should take care
                         * care of the incremented refcount caused by either
                         * lookupKeyByPattern or createStringObject("",0) */
                        listTypePush(sobj,val,REDIS_TAIL);
                        decrRefCount(val);
                    } else {
                        /* always fails */
                        redisAssert(sop->type == REDIS_SORT_GET);
                    }
                }
            }
        }
        setKey(c->db,storekey,sobj);
        decrRefCount(sobj);
        /* Note: we add 1 because the DB is dirty anyway since even if the
         * SORT result is empty a new key is set and maybe the old content
         * replaced. */
        server.dirty += 1+outputlen;
        addReplyLongLong(c,outputlen);
    }

    /* Cleanup */
    if (sortval->type == REDIS_LIST || sortval->type == REDIS_SET)
        for (j = 0; j < vectorlen; j++)
            decrRefCount(vector[j].obj);
    decrRefCount(sortval);
    listRelease(operations);
    for (j = 0; j < vectorlen; j++) {
        if (alpha && vector[j].u.cmpobj)
            decrRefCount(vector[j].u.cmpobj);
    }
    zfree(vector);
}
Esempio n. 24
0
/* This command implements SCAN, HSCAN and SSCAN commands.
 * If object 'o' is passed, then it must be a Hash or Set object, otherwise
 * if 'o' is NULL the command will operate on the dictionary associated with
 * the current database.
 *
 * When 'o' is not NULL the function assumes that the first argument in
 * the client arguments vector is a key so it skips it before iterating
 * in order to parse options.
 *
 * In the case of a Hash object the function returns both the field and value
 * of every element on the Hash. */
void scanGenericCommand(client *c, robj *o, unsigned long cursor) {
    int i, j;
    list *keys = listCreate();
    listNode *node, *nextnode;
    long count = 10;
    sds pat = NULL;
    int patlen = 0, use_pattern = 0;
    dict *ht;

    /* Object must be NULL (to iterate keys names), or the type of the object
     * must be Set, Sorted Set, or Hash. */
    serverAssert(o == NULL || o->type == OBJ_SET || o->type == OBJ_HASH ||
                o->type == OBJ_ZSET);

    /* Set i to the first option argument. The previous one is the cursor. */
    i = (o == NULL) ? 2 : 3; /* Skip the key argument if needed. */

    /* Step 1: Parse options. */
    while (i < c->argc) {
        j = c->argc - i;
        if (!strcasecmp(c->argv[i]->ptr, "count") && j >= 2) {
            if (getLongFromObjectOrReply(c, c->argv[i+1], &count, NULL)
                != C_OK)
            {
                goto cleanup;
            }

            if (count < 1) {
                addReply(c,shared.syntaxerr);
                goto cleanup;
            }

            i += 2;
        } else if (!strcasecmp(c->argv[i]->ptr, "match") && j >= 2) {
            pat = c->argv[i+1]->ptr;
            patlen = sdslen(pat);

            /* The pattern always matches if it is exactly "*", so it is
             * equivalent to disabling it. */
            use_pattern = !(pat[0] == '*' && patlen == 1);

            i += 2;
        } else {
            addReply(c,shared.syntaxerr);
            goto cleanup;
        }
    }

    /* Step 2: Iterate the collection.
     *
     * Note that if the object is encoded with a ziplist, intset, or any other
     * representation that is not a hash table, we are sure that it is also
     * composed of a small number of elements. So to avoid taking state we
     * just return everything inside the object in a single call, setting the
     * cursor to zero to signal the end of the iteration. */

    /* Handle the case of a hash table. */
    ht = NULL;
    if (o == NULL) {
        ht = c->db->dict;
    } else if (o->type == OBJ_SET && o->encoding == OBJ_ENCODING_HT) {
        ht = o->ptr;
    } else if (o->type == OBJ_HASH && o->encoding == OBJ_ENCODING_HT) {
        ht = o->ptr;
        count *= 2; /* We return key / value for this type. */
    } else if (o->type == OBJ_ZSET && o->encoding == OBJ_ENCODING_SKIPLIST) {
        zset *zs = o->ptr;
        ht = zs->dict;
        count *= 2; /* We return key / value for this type. */
    }

    if (ht) {
        void *privdata[2];
        /* We set the max number of iterations to ten times the specified
         * COUNT, so if the hash table is in a pathological state (very
         * sparsely populated) we avoid to block too much time at the cost
         * of returning no or very few elements. */
        long maxiterations = count*10;

        /* We pass two pointers to the callback: the list to which it will
         * add new elements, and the object containing the dictionary so that
         * it is possible to fetch more data in a type-dependent way. */
        privdata[0] = keys;
        privdata[1] = o;
        do {
            cursor = dictScan(ht, cursor, scanCallback, privdata);
        } while (cursor &&
              maxiterations-- &&
              listLength(keys) < (unsigned long)count);
    } else if (o->type == OBJ_SET) {
        int pos = 0;
        int64_t ll;

        while(intsetGet(o->ptr,pos++,&ll))
            listAddNodeTail(keys,createStringObjectFromLongLong(ll));
        cursor = 0;
    } else if (o->type == OBJ_HASH || o->type == OBJ_ZSET) {
        unsigned char *p = ziplistIndex(o->ptr,0);
        unsigned char *vstr;
        unsigned int vlen;
        long long vll;

        while(p) {
            ziplistGet(p,&vstr,&vlen,&vll);
            listAddNodeTail(keys,
                (vstr != NULL) ? createStringObject((char*)vstr,vlen) :
                                 createStringObjectFromLongLong(vll));
            p = ziplistNext(o->ptr,p);
        }
        cursor = 0;
    } else {
        serverPanic("Not handled encoding in SCAN.");
    }

    /* Step 3: Filter elements. */
    node = listFirst(keys);
    while (node) {
        robj *kobj = listNodeValue(node);
        nextnode = listNextNode(node);
        int filter = 0;

        /* Filter element if it does not match the pattern. */
        if (!filter && use_pattern) {
            if (sdsEncodedObject(kobj)) {
                if (!stringmatchlen(pat, patlen, kobj->ptr, sdslen(kobj->ptr), 0))
                    filter = 1;
            } else {
                char buf[LONG_STR_SIZE];
                int len;

                serverAssert(kobj->encoding == OBJ_ENCODING_INT);
                len = ll2string(buf,sizeof(buf),(long)kobj->ptr);
                if (!stringmatchlen(pat, patlen, buf, len, 0)) filter = 1;
            }
        }

        /* Filter element if it is an expired key. */
        if (!filter && o == NULL && expireIfNeeded(c->db, kobj)) filter = 1;

        /* Remove the element and its associted value if needed. */
        if (filter) {
            decrRefCount(kobj);
            listDelNode(keys, node);
        }

        /* If this is a hash or a sorted set, we have a flat list of
         * key-value elements, so if this element was filtered, remove the
         * value, or skip it if it was not filtered: we only match keys. */
        if (o && (o->type == OBJ_ZSET || o->type == OBJ_HASH)) {
            node = nextnode;
            nextnode = listNextNode(node);
            if (filter) {
                kobj = listNodeValue(node);
                decrRefCount(kobj);
                listDelNode(keys, node);
            }
        }
        node = nextnode;
    }

    /* Step 4: Reply to the client. */
    addReplyMultiBulkLen(c, 2);
    addReplyBulkLongLong(c,cursor);

    addReplyMultiBulkLen(c, listLength(keys));
    while ((node = listFirst(keys)) != NULL) {
        robj *kobj = listNodeValue(node);
        addReplyBulk(c, kobj);
        decrRefCount(kobj);
        listDelNode(keys, node);
    }

cleanup:
    listSetFreeMethod(keys,decrRefCountVoid);
    listRelease(keys);
}
Esempio n. 25
0
/* Initialize the slow log. This function should be called a single time
 * at server startup. */
void slowlogInit(void) {
    server.slowlog = listCreate();
    server.slowlog_entry_id = 0;
    listSetFreeMethod(server.slowlog,slowlogFreeEntry);
}
Esempio n. 26
0
int main(int argc, const char **argv) {
    int i;
    char *data, *cmd;
    int len;

    client c;

    srandom(time(NULL));
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    config.numclients = 50;
    config.requests = 100000;
    config.liveclients = 0;
    config.el = aeCreateEventLoop(1024*10);
    aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL);
    config.keepalive = 1;
    config.datasize = 3;
    config.pipeline = 1;
    config.randomkeys = 0;
    config.randomkeys_keyspacelen = 0;
    config.quiet = 0;
    config.csv = 0;
    config.loop = 0;
    config.idlemode = 0;
    config.latency = NULL;
    config.clients = listCreate();
    config.hostip = "127.0.0.1";
    config.hostport = 6379;
    config.hostsocket = NULL;
    config.tests = NULL;
    config.dbnum = 0;
    config.auth = NULL;

    i = parseOptions(argc,argv);
    argc -= i;
    argv += i;

    config.latency = zmalloc(sizeof(long long)*config.requests);

    if (config.keepalive == 0) {
        printf("WARNING: keepalive disabled, you probably need 'echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse' for Linux and 'sudo sysctl -w net.inet.tcp.msl=1000' for Mac OS X in order to use a lot of clients/requests\n");
    }

    if (config.idlemode) {
        printf("Creating %d idle connections and waiting forever (Ctrl+C when done)\n", config.numclients);
        c = createClient("",0,NULL); /* will never receive a reply */
        createMissingClients(c);
        aeMain(config.el);
        /* and will wait for every */
    }

    /* Run benchmark with command in the remainder of the arguments. */
    if (argc) {
        sds title = sdsnew(argv[0]);
        for (i = 1; i < argc; i++) {
            title = sdscatlen(title, " ", 1);
            title = sdscatlen(title, (char*)argv[i], strlen(argv[i]));
        }

        do {
            len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
            benchmark(title,cmd,len);
            free(cmd);
        } while(config.loop);

        return 0;
    }

    /* Run default benchmark suite. */
    data = zmalloc(config.datasize+1);
    do {
        memset(data,'x',config.datasize);
        data[config.datasize] = '\0';

        if (test_is_selected("ping_inline") || test_is_selected("ping"))
            benchmark("PING_INLINE","PING\r\n",6);

        if (test_is_selected("ping_mbulk") || test_is_selected("ping")) {
            len = redisFormatCommand(&cmd,"PING");
            benchmark("PING_BULK",cmd,len);
            free(cmd);
        }

        if (test_is_selected("set")) {
            len = redisFormatCommand(&cmd,"SET key:__rand_int__ %s",data);
            benchmark("SET",cmd,len);
            free(cmd);
        }

        if (test_is_selected("get")) {
            len = redisFormatCommand(&cmd,"GET key:__rand_int__");
            benchmark("GET",cmd,len);
            free(cmd);
        }

        if (test_is_selected("incr")) {
            len = redisFormatCommand(&cmd,"INCR counter:__rand_int__");
            benchmark("INCR",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lpush")) {
            len = redisFormatCommand(&cmd,"LPUSH mylist %s",data);
            benchmark("LPUSH",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lpop")) {
            len = redisFormatCommand(&cmd,"LPOP mylist");
            benchmark("LPOP",cmd,len);
            free(cmd);
        }

        if (test_is_selected("sadd")) {
            len = redisFormatCommand(&cmd,
                "SADD myset element:__rand_int__");
            benchmark("SADD",cmd,len);
            free(cmd);
        }

        if (test_is_selected("spop")) {
            len = redisFormatCommand(&cmd,"SPOP myset");
            benchmark("SPOP",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lrange") ||
            test_is_selected("lrange_100") ||
            test_is_selected("lrange_300") ||
            test_is_selected("lrange_500") ||
            test_is_selected("lrange_600"))
        {
            len = redisFormatCommand(&cmd,"LPUSH mylist %s",data);
            benchmark("LPUSH (needed to benchmark LRANGE)",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lrange") || test_is_selected("lrange_100")) {
            len = redisFormatCommand(&cmd,"LRANGE mylist 0 99");
            benchmark("LRANGE_100 (first 100 elements)",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lrange") || test_is_selected("lrange_300")) {
            len = redisFormatCommand(&cmd,"LRANGE mylist 0 299");
            benchmark("LRANGE_300 (first 300 elements)",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lrange") || test_is_selected("lrange_500")) {
            len = redisFormatCommand(&cmd,"LRANGE mylist 0 449");
            benchmark("LRANGE_500 (first 450 elements)",cmd,len);
            free(cmd);
        }

        if (test_is_selected("lrange") || test_is_selected("lrange_600")) {
            len = redisFormatCommand(&cmd,"LRANGE mylist 0 599");
            benchmark("LRANGE_600 (first 600 elements)",cmd,len);
            free(cmd);
        }

        if (test_is_selected("mset")) {
            const char *argv[21];
            argv[0] = "MSET";
            for (i = 1; i < 21; i += 2) {
                argv[i] = "key:__rand_int__";
                argv[i+1] = data;
            }
            len = redisFormatCommandArgv(&cmd,21,argv,NULL);
            benchmark("MSET (10 keys)",cmd,len);
            free(cmd);
        }

        if (!config.csv) printf("\n");
    } while(config.loop);

    return 0;
}
Esempio n. 27
0
File: kp8.c Progetto: BlagoProg/MAI
int main(void)
{
	const int N = 10;
	int i, isFound, action, pos, arg;
	List list;
	Iterator it1, it2;

	listCreate(&list, N);

	do
	{
		printf("Меню:\n");
		printf("1) Вставить элемент\n");
		printf("2) Удалить элемент\n");
		printf("3) Печать списка\n");
		printf("4) Подсчет длины списка\n");
		printf("5) Выполнить задание над списком\n");
		printf("6) Выход\n");
		printf("Выберите действие: ");
		scanf("%d", &action);

		switch (action)
		{
			case 1:
			{
				printf("Введите позицию элемента: ");
				scanf("%d", &pos);
				printf("Введите значение элемента (1 - true, 0 - false): ");
				scanf("%d", &arg);

				if (arg != 0 && arg != 1)
					printf("Ошибка. Введено недопустимое значение\n");
				else
					listInsert(&list, pos - 1, arg);

				break;
			}

			case 2:
			{
				printf("Введите номер элемента: ");
				scanf("%d", &pos);

				listRemove(&list, pos - 1);

				break;
			}

			case 3:
			{
				if (listEmpty(&list))
					printf("Список пуст\n");
				else
					listPrint(&list);

				break;
			}

			case 4:
			{
				printf("Длина списка: %d\n", listSize(&list));

				break;
			}

			case 5:
			{
				it1 = itFirst(&list);
				it2 = it1;

				itNext(&it2);

				for (i = 0; i < listSize(&list) / 2; i++)
				{
					listSwapElems(&it1, &it2);

					itNext(&it1);
					itNext(&it1);
					itNext(&it2);
					itNext(&it2);
				}

				break;
			}

			case 6: break;

			default:
			{
				printf("Ошибка. Такого пункта меню не существует\n");

				break;
			}
		}
	}
	while (action != 6);

	listDestroy(&list);

	return 0;
}