haproxy/tests/unit/test-list.c

99 lines
1.9 KiB
C
Raw Normal View History

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define USE_THREAD
#include <haproxy/list.h>
/* Stress test the mt_lists.
* Compile from the haproxy directory with :
* cc -I../../include test-list.c -pthread -O2 -o test-list
* The only argument it takes is the number of threads to be used.
* ./test-list 4
*/
struct mt_list pouet_list = MT_LIST_HEAD_INIT(pouet_list);
#define MAX_ACTION 5000000
__thread unsigned int tid;
struct pouet_lol {
struct mt_list list_elt;
};
void *thread(void *pouet)
{
struct pouet_lol *lol;
struct mt_list *elt1, elt2;
tid = (uintptr_t)pouet;
int i = 0;
for (int i = 0; i < MAX_ACTION; i++) {
struct pouet_lol *lol;
struct mt_list *elt1, elt2;
switch (random() % 4) {
case 0:
lol = malloc(sizeof(*lol));
MT_LIST_INIT(&lol->list_elt);
MINOR: lists: rename some MT_LIST operations to clarify them Initially when mt_lists were added, their purpose was to be used with the scheduler, where anyone may concurrently add the same tasklet, so it sounded natural to implement a check in MT_LIST_ADD{,Q}. Later their usage was extended and MT_LIST_ADD{,Q} started to be used on situations where the element to be added was exclusively owned by the one performing the operation so a conflict was impossible. This became more obvious with the idle connections and the new macro was called MT_LIST_ADDQ_NOCHECK. But this remains confusing and at many places it's not expected that an MT_LIST_ADD could possibly fail, and worse, at some places we start by initializing it before adding (and the test is superflous) so let's rename them to something more conventional to denote the presence of the check or not: MT_LIST_ADD{,Q} : inconditional operation, the caller owns the element, and doesn't care about the element's current state (exactly like LIST_ADD) MT_LIST_TRY_ADD{,Q}: only perform the operation if the element is not already added or in the process of being added. This means that the previously "safe" MT_LIST_ADD{,Q} are not "safe" anymore. This also means that in case of backport mistakes in the future causing this to be overlooked, the slower and safer functions will still be used by default. Note that the missing unchecked MT_LIST_ADD macro was added. The rest of the code will have to be reviewed so that a number of callers of MT_LIST_TRY_ADDQ are changed to MT_LIST_ADDQ to remove the unneeded test.
2020-07-10 06:10:29 +00:00
MT_LIST_TRY_ADD(&pouet_list, &lol->list_elt);
break;
case 1:
lol = malloc(sizeof(*lol));
MT_LIST_INIT(&lol->list_elt);
MINOR: lists: rename some MT_LIST operations to clarify them Initially when mt_lists were added, their purpose was to be used with the scheduler, where anyone may concurrently add the same tasklet, so it sounded natural to implement a check in MT_LIST_ADD{,Q}. Later their usage was extended and MT_LIST_ADD{,Q} started to be used on situations where the element to be added was exclusively owned by the one performing the operation so a conflict was impossible. This became more obvious with the idle connections and the new macro was called MT_LIST_ADDQ_NOCHECK. But this remains confusing and at many places it's not expected that an MT_LIST_ADD could possibly fail, and worse, at some places we start by initializing it before adding (and the test is superflous) so let's rename them to something more conventional to denote the presence of the check or not: MT_LIST_ADD{,Q} : inconditional operation, the caller owns the element, and doesn't care about the element's current state (exactly like LIST_ADD) MT_LIST_TRY_ADD{,Q}: only perform the operation if the element is not already added or in the process of being added. This means that the previously "safe" MT_LIST_ADD{,Q} are not "safe" anymore. This also means that in case of backport mistakes in the future causing this to be overlooked, the slower and safer functions will still be used by default. Note that the missing unchecked MT_LIST_ADD macro was added. The rest of the code will have to be reviewed so that a number of callers of MT_LIST_TRY_ADDQ are changed to MT_LIST_ADDQ to remove the unneeded test.
2020-07-10 06:10:29 +00:00
MT_LIST_TRY_ADDQ(&pouet_list, &lol->list_elt);
break;
case 2:
lol = MT_LIST_POP(&pouet_list, struct pouet_lol *, list_elt);
if (lol)
free(lol);
break;
case 3:
mt_list_for_each_entry_safe(lol, &pouet_list, list_elt, elt1, elt2)
{
if (random() % 2) {
MT_LIST_DEL_SAFE(elt1);
free(lol);
}
if (random() % 2) {
break;
}
}
break;
default:
break;
}
}
}
int main(int argc, char *argv[])
{
int nb;
pthread_t *pth;
srandom(time(NULL));
if (argc != 2) {
printf("Usage: %s <nb_threads>\n", argv[0]);
exit(1);
}
nb = atoi(argv[1]);
#if 0
if (nb < 2) {
printf("Need at least 2 threads.\n");
exit(1);
}
#endif
pth = malloc(nb * sizeof(*pth));
if (pth == NULL) {
printf("Shot failed to connect.\n");
exit(1);
}
for (int i = 0; i < nb; i++) {
pthread_create(&pth[i], NULL, thread, (void *)(uintptr_t)i);
}
for (int i = 0; i < nb; i++)
pthread_join(pth[i], NULL);
return 0;
}