Recently, I wanted to learn Redis by using Redis's source code. Although the usual work is not used much, but the Redis is still more interested in, after all, its performance is good. Redis is an open source project that we can use to understand Redis through source code. I will later through their own study, write some about the Redis source code posts. The main content of the post is the analysis of the code design, and does not explain the source code in detail. If there is a wrong place, please correct me. Source code is Reids 3.0.3 version.
Adlist
One, adlist, double chain list
The data structure is defined as follows:
node typedef struct LISTNODE {struct ListNode *prev; struct ListNode *next; void *value;} listnode;//iterator typedef struct LISTITER {ListNode *next; int direction;//iterator Direction} listiter;//linked list typedef struct List {ListNode *head; ListNode *tail; void * (*dup) (void *ptr);//copy function void (*free) (void *ptr);//Destroy function int (*match) (void *ptr, void *key);//matching function unsigned Long Len;} List
With the general two-way linked list is not very different. Redis's doubly linked list is designed to be a more generic linked list, so value is a pointer, not limited to the actual type of value, in addition to the list provides a copy, destroy, matching function, so that the list of value can support different memory management methods, or some callbacks.
In addition, iterators are provided, and the iterator of a linked list is usually not a random iterator, but only one element can jump one element at a to. Since this is a doubly linked list, the iterator can jump forward or jump backwards, but redis only provides functions that jump backwards. Iterators also support forward, reverse, and need to specify the direction through the direction field. The combination of direction also enables the iterator to jump forward, presumably so that Adlist does not provide a function to jump forward.
Second, the related operation function of adlist
There are two main categories, one is a macro, a class is a function, see the code is relatively simple, here is not detailed code. Just simply enumerate the functions.
/* prototypes */list *listcreate (void);//Create linked list void listrelease (list *list);//Destroy List of lists *listaddnodehead (List *list, void *value);//Insert element List *listaddnodetail into the table header (list * List, void *value);//Insert Element List *listinsertnode (List *list, listnode *old_node) toward the end of the table, void *value, int after) //insert to the specified position Void listdelnode (list *list, Listnode *node);//Delete node listiter *listgetiterator (list *list, int direction);// Iterator Listnode *listnext (listiter *iter);//iterator jumps backward, next Void listreleaseiterator (Listiter *iter) ;//Destroy Iterator List *listdup (list *orig);//Copy list Listnode *listsearchkey (list *list, void *key);//Query Listnode *listindex (LIST *LIST, LONG INDEX);//Gets the node at the specified location void Listrewind (List *list, listiter *li);//The iterator returns the header, forward iterator Void listrewindtail (list *list,  LISTITER *LI);//iterator to footer, reverse iterator VoId listrotate (list *list);//rotate linked list, put the footer element to the table header
Iii. Adlist provides memory management of objects
Let's talk about how Adlist manages memory through Dup,free.
There are three places to be aware of:
A. When inserting an element, value in node is directly assigned the value of the incoming parameter, and the DUP function is not called.
B. In listdup, if the DUP function exists, call the DUP function to copy value.
C. Listdelnode and Listrelease, if the free function exists, the value is destroyed by the free method.
Through the above three points, you can implement some simple memory management methods, mainly the application and destruction of object memory management. Here is a brief introduction to the usage.
1) Adlist is not responsible for application and destruction
such as the following code (code does not pass the compilation test, may be wrong)
Char s[] = "Hello world"; list *lst = Listcreate ();//list free,dup function is empty listaddnodetail (LST, &s[0]); Listaddnodetail ( LST, &s[1]); Listaddnodetail (LST, &s[2]); Listaddnodetail (LST, &s[3]);//do some thing with lstlist *lst2 = Listdup (LST);//do some thing with lst2listrelease (LST); Listrelease (LST2);
In the example above, Adlist is not responsible for the application and destruction of the value memory space, which is managed entirely by the caller. The element specified by value is required to have a longer life cycle than adlist, otherwise the value specified in Adlist may be invalidated.
2) adlist responsible for dynamic application and destruction
As the following code, (the code does not pass the compilation test, possibly wrong):
Void addstringtolisthead (list *lst, char *s) { char *snew = (char*) malloc (strlen (s) +1); assert (snew); strcpy (snew,s); listaddnodehead (list,snew);} Void *stringdup (void *s) { char *sorg = (char*) s; char *snew = (char*) malloc (strlen (sorg) +1); assert (snew); strcpy (snew,sorg); return snew; } Void *stringfree (void *s) { if (s) free (s);} Int stringmatch (VOID *S1, VOID *S2) { if (s1 &&  S2) return strcmp ((char*) s1, (char*) s2) == 0 ? 1 : 0; if (!S1 && !S2)  RETURN 1;    RETURN&Nbsp;0;} List *lst = listcreate (); Listsetdupmethod (lst, stringdup); Listsetfreemethod (lst, Stringfree); Listsetmatchmethod (Lst, stringmatch); Addstringtolisthead (LST, "Hello"); Addstringtolisthead (LST , "World");// do somethinglist *lst2 = listdup (LST);// do Somethinglistrelease (LST); Listrelease (LST2);
In the example above, the dynamically generated space is inserted into adlist, which is managed entirely by Adlist, and the caller should not hold or delete the dynamic space pointer for a long time, otherwise it will cause management confusion. It is important to note that Adlist's DUP function needs to be deeply copied at copy time, otherwise a dynamic request space is saved by two pointers for a long time, as long as one of the pointers is free, another pointer is invalidated, the re-access or the free fail pointer will be faulted.
3) failed to support reference counting friendly
The reason why the reference counting was not friendly is that when inserting an element, no related function is called to increase the reference count. Use DUP to implement the reference count plus 1 when the list is copied, minus 1 when the node is deleted by the free reference count. However, when inserting an element, the pointer is held because no DUP is called or other functions are used to make the reference count 1,value, which makes adlist not friendly to support reference counting. However, this can be supported by other means, such as the reference counter plus 1 before inserting, and the reference count minus 1 if the insert fails.
Four, easy to produce memory fragmentation.
Do not consider value first, just look at node. As you can see, adlist is required to apply for node space when inserting elements or copying linked lists, and freeing up space when deleting nodes. For continuous insertion and deletion, if all zmalloc, zfree functions do not make a good memory management strategy, it is easy to create content fragmentation.
V. Summary
In addition to providing caller-defined methods in memory management, Adlist is not much different from the general two-way list, which is characterized as described above and is no longer described here.
This article is from the "Chhquan" blog, make sure to keep this source http://chhquan.blog.51cto.com/1346841/1771106
Adlist of Redis underlying data structure