/[LeafOK_CVS]/lbbs/src/user_list.c
ViewVC logotype

Contents of /lbbs/src/user_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.44 - (show annotations)
Thu Nov 20 11:31:56 2025 UTC (3 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.43: +52 -0 lines
Content type: text/x-csrc
Add mechanism to detect and resolve dead lock caused by POSIX semaphore.

1 /* SPDX-License-Identifier: GPL-3.0-or-later */
2 /*
3 * user_list
4 * - data model and basic operations of (online) user list
5 *
6 * Copyright (C) 2004-2025 Leaflet <leaflet@leafok.com>
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "common.h"
14 #include "database.h"
15 #include "log.h"
16 #include "trie_dict.h"
17 #include "user_list.h"
18 #include "user_stat.h"
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <sys/mman.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27
28 #ifdef HAVE_SYSTEM_V
29 #include <sys/sem.h>
30
31 #ifdef _SEM_SEMUN_UNDEFINED
32 union semun
33 {
34 int val; /* Value for SETVAL */
35 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
36 unsigned short *array; /* Array for GETALL, SETALL */
37 struct seminfo *__buf; /* Buffer for IPC_INFO
38 (Linux-specific) */
39 };
40 #endif // #ifdef _SEM_SEMUN_UNDEFINED
41
42 #else
43 #include <semaphore.h>
44 #endif
45
46 enum _user_list_constant_t
47 {
48 USER_LIST_TRY_LOCK_WAIT_TIME = 1, // second
49 USER_LIST_TRY_LOCK_TIMES = 10,
50 USER_LIST_DEAD_LOCK_TIMEOUT = 15, // second
51 };
52
53 struct user_list_pool_t
54 {
55 size_t shm_size;
56 #ifndef HAVE_SYSTEM_V
57 sem_t sem;
58 uint16_t read_lock_count;
59 uint16_t write_lock_count;
60 #else
61 int semid;
62 #endif
63 USER_LIST user_list[2];
64 int user_list_index_current;
65 int user_list_index_new;
66 USER_ONLINE_LIST user_online_list[2];
67 int user_online_list_index_current;
68 int user_online_list_index_new;
69 USER_STAT_MAP user_stat_map;
70 int user_login_count;
71 };
72 typedef struct user_list_pool_t USER_LIST_POOL;
73
74 static char user_list_shm_name[FILE_NAME_LEN];
75 static USER_LIST_POOL *p_user_list_pool = NULL;
76 static TRIE_NODE *p_trie_action_dict = NULL;
77
78 typedef struct user_action_map_t
79 {
80 char name[BBS_current_action_max_len + 1];
81 char title[BBS_current_action_max_len + 1];
82 } USER_ACTION_MAP;
83
84 const USER_ACTION_MAP user_action_map[] =
85 {
86 {"ARTICLE_FAVOR", "浏览收藏"},
87 {"BBS_NET", "站点穿梭"},
88 {"CHICKEN", "电子小鸡"},
89 {"EDIT_ARTICLE", "修改文章"},
90 {"LOGIN", "进入大厅"},
91 {"MENU", "菜单选择"},
92 {"POST_ARTICLE", "撰写文章"},
93 {"REPLY_ARTICLE", "回复文章"},
94 {"USER_LIST", "查花名册"},
95 {"USER_ONLINE", "环顾四周"},
96 {"VIEW_ARTICLE", "阅读文章"},
97 {"VIEW_FILE", "查看文档"},
98 {"WWW", "Web浏览"}};
99
100 const int user_action_map_size = sizeof(user_action_map) / sizeof(USER_ACTION_MAP);
101
102 static int user_list_try_rd_lock(int wait_sec);
103 static int user_list_try_rw_lock(int wait_sec);
104 static int user_list_rd_unlock(void);
105 static int user_list_rw_unlock(void);
106 static int user_list_rd_lock(void);
107 static int user_list_rw_lock(void);
108 #ifndef HAVE_SYSTEM_V
109 static int user_list_reset_lock(void);
110 #endif
111
112 static int user_list_load(MYSQL *db, USER_LIST *p_list);
113 static int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_online_list);
114 static int user_login_count_load(MYSQL *db);
115
116 static int user_info_index_uid_comp(const void *ptr1, const void *ptr2)
117 {
118 const USER_INFO_INDEX_UID *p1 = ptr1;
119 const USER_INFO_INDEX_UID *p2 = ptr2;
120
121 if (p1->uid < p2->uid)
122 {
123 return -1;
124 }
125 else if (p1->uid > p2->uid)
126 {
127 return 1;
128 }
129 else if (p1->id < p2->id)
130 {
131 return -1;
132 }
133 else if (p1->id > p2->id)
134 {
135 return 1;
136 }
137 return 0;
138 }
139
140 int user_list_load(MYSQL *db, USER_LIST *p_list)
141 {
142 MYSQL_RES *rs = NULL;
143 MYSQL_ROW row;
144 char sql[SQL_BUFFER_LEN];
145 int ret = 0;
146 int i;
147 int j;
148 int32_t last_uid;
149 size_t intro_buf_offset;
150 size_t intro_len;
151
152 if (db == NULL || p_list == NULL)
153 {
154 log_error("NULL pointer error\n");
155 return -1;
156 }
157
158 if (p_list->user_count > 0)
159 {
160 last_uid = p_list->users[p_list->user_count - 1].uid;
161 }
162 else
163 {
164 last_uid = -1;
165 }
166
167 snprintf(sql, sizeof(sql),
168 "SELECT user_list.UID AS UID, username, nickname, gender, gender_pub, life, exp, visit_count, "
169 "UNIX_TIMESTAMP(signup_dt), UNIX_TIMESTAMP(last_login_dt), UNIX_TIMESTAMP(last_logout_dt), "
170 "UNIX_TIMESTAMP(birthday), `introduction` "
171 "FROM user_list INNER JOIN user_pubinfo ON user_list.UID = user_pubinfo.UID "
172 "INNER JOIN user_reginfo ON user_list.UID = user_reginfo.UID "
173 "WHERE enable ORDER BY username");
174
175 if (mysql_query(db, sql) != 0)
176 {
177 log_error("Query user info error: %s\n", mysql_error(db));
178 ret = -1;
179 goto cleanup;
180 }
181
182 if ((rs = mysql_use_result(db)) == NULL)
183 {
184 log_error("Get user info data failed\n");
185 ret = -1;
186 goto cleanup;
187 }
188
189 intro_buf_offset = 0;
190 i = 0;
191 while ((row = mysql_fetch_row(rs)))
192 {
193 // record
194 p_list->users[i].id = i;
195 p_list->users[i].uid = atoi(row[0]);
196 strncpy(p_list->users[i].username, row[1], sizeof(p_list->users[i].username) - 1);
197 p_list->users[i].username[sizeof(p_list->users[i].username) - 1] = '\0';
198 strncpy(p_list->users[i].nickname, row[2], sizeof(p_list->users[i].nickname) - 1);
199 p_list->users[i].nickname[sizeof(p_list->users[i].nickname) - 1] = '\0';
200 p_list->users[i].gender = row[3][0];
201 p_list->users[i].gender_pub = (int8_t)(row[4] == NULL ? 0 : atoi(row[4]));
202 p_list->users[i].life = (row[5] == NULL ? 0 : atoi(row[5]));
203 p_list->users[i].exp = (row[6] == NULL ? 0 : atoi(row[6]));
204 p_list->users[i].visit_count = (row[7] == NULL ? 0 : atoi(row[7]));
205 p_list->users[i].signup_dt = (row[8] == NULL ? 0 : atol(row[8]));
206 p_list->users[i].last_login_dt = (row[9] == NULL ? 0 : atol(row[9]));
207 p_list->users[i].last_logout_dt = (row[10] == NULL ? 0 : atol(row[10]));
208 p_list->users[i].birthday = (row[11] == NULL ? 0 : atol(row[11]));
209 intro_len = strlen((row[12] == NULL ? "" : row[12]));
210 if (intro_len >= sizeof(p_list->user_intro_buf) - 1 - intro_buf_offset)
211 {
212 log_error("OOM for user introduction: len=%d, i=%d\n", intro_len, i);
213 break;
214 }
215 memcpy(p_list->user_intro_buf + intro_buf_offset,
216 (row[12] == NULL ? "" : row[12]),
217 intro_len + 1);
218 p_list->users[i].intro = p_list->user_intro_buf + intro_buf_offset;
219 intro_buf_offset += (intro_len + 1);
220
221 i++;
222 if (i >= BBS_max_user_count)
223 {
224 log_error("Too many users, exceed limit %d\n", BBS_max_user_count);
225 break;
226 }
227 }
228 mysql_free_result(rs);
229 rs = NULL;
230
231 if (i != p_list->user_count || p_list->users[i - 1].uid != last_uid) // Count of users changed
232 {
233 // Rebuild index
234 for (j = 0; j < i; j++)
235 {
236 p_list->index_uid[j].uid = p_list->users[j].uid;
237 p_list->index_uid[j].id = j;
238 }
239
240 qsort(p_list->index_uid, (size_t)i, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
241
242 #ifdef _DEBUG
243 log_error("Rebuild index of %d users, last_uid=%d\n", i, p_list->users[i - 1].uid);
244 #endif
245 }
246
247 p_list->user_count = i;
248
249 #ifdef _DEBUG
250 log_error("Loaded %d users\n", p_list->user_count);
251 #endif
252
253 cleanup:
254 mysql_free_result(rs);
255
256 return ret;
257 }
258
259 int user_online_list_load(MYSQL *db, USER_ONLINE_LIST *p_online_list)
260 {
261 MYSQL_RES *rs = NULL;
262 MYSQL_ROW row;
263 char sql[SQL_BUFFER_LEN];
264 int ret = 0;
265 int i;
266 int j;
267 int user_cnt;
268 int guest_cnt;
269
270 if (db == NULL || p_online_list == NULL)
271 {
272 log_error("NULL pointer error\n");
273 return -1;
274 }
275
276 snprintf(sql, sizeof(sql),
277 "SELECT SID, UID, ip, current_action, UNIX_TIMESTAMP(login_tm), "
278 "UNIX_TIMESTAMP(last_tm) FROM user_online "
279 "WHERE last_tm >= SUBDATE(NOW(), INTERVAL %d SECOND) "
280 "ORDER BY last_tm DESC",
281 BBS_user_off_line);
282
283 if (mysql_query(db, sql) != 0)
284 {
285 log_error("Query user online error: %s\n", mysql_error(db));
286 ret = -1;
287 goto cleanup;
288 }
289
290 if ((rs = mysql_use_result(db)) == NULL)
291 {
292 log_error("Get user online data failed\n");
293 ret = -1;
294 goto cleanup;
295 }
296
297 i = 0;
298 user_cnt = 0;
299 guest_cnt = 0;
300 while ((row = mysql_fetch_row(rs)))
301 {
302 if (atoi(row[1]) == 0) // guest
303 {
304 guest_cnt++;
305 continue;
306 }
307 else
308 {
309 user_cnt++;
310 }
311
312 p_online_list->users[i].id = i;
313 strncpy(p_online_list->users[i].session_id, row[0], sizeof(p_online_list->users[i].session_id) - 1);
314 p_online_list->users[i].session_id[sizeof(p_online_list->users[i].session_id) - 1] = '\0';
315
316 if ((ret = query_user_info_by_uid(atoi(row[1]), &(p_online_list->users[i].user_info), NULL, 0)) <= 0)
317 {
318 log_error("query_user_info_by_uid(%d) error\n", atoi(row[1]));
319 continue;
320 }
321
322 strncpy(p_online_list->users[i].ip, row[2], sizeof(p_online_list->users[i].ip) - 1);
323 p_online_list->users[i].ip[sizeof(p_online_list->users[i].ip) - 1] = '\0';
324
325 strncpy(p_online_list->users[i].current_action, row[3], sizeof(p_online_list->users[i].current_action) - 1);
326 p_online_list->users[i].current_action[sizeof(p_online_list->users[i].current_action) - 1] = '\0';
327 p_online_list->users[i].current_action_title = NULL;
328 if (p_online_list->users[i].current_action[0] == '\0')
329 {
330 p_online_list->users[i].current_action_title = "";
331 }
332 else if (trie_dict_get(p_trie_action_dict, p_online_list->users[i].current_action, (int64_t *)(&(p_online_list->users[i].current_action_title))) < 0)
333 {
334 log_error("trie_dict_get(p_trie_action_dict, %s) error on session_id=%s\n",
335 p_online_list->users[i].current_action, p_online_list->users[i].session_id);
336 continue;
337 }
338
339 p_online_list->users[i].login_tm = (row[4] == NULL ? 0 : atol(row[4]));
340 p_online_list->users[i].last_tm = (row[5] == NULL ? 0 : atol(row[5]));
341
342 i++;
343 if (i >= BBS_max_user_online_count)
344 {
345 log_error("Too many online users, exceed limit %d\n", BBS_max_user_online_count);
346 break;
347 }
348 }
349 mysql_free_result(rs);
350 rs = NULL;
351
352 if (user_cnt > 0)
353 {
354 // Rebuild index
355 for (j = 0; j < user_cnt; j++)
356 {
357 p_online_list->index_uid[j].uid = p_online_list->users[j].user_info.uid;
358 p_online_list->index_uid[j].id = j;
359 }
360
361 qsort(p_online_list->index_uid, (size_t)user_cnt, sizeof(USER_INFO_INDEX_UID), user_info_index_uid_comp);
362 }
363
364 p_online_list->user_count = user_cnt;
365 p_online_list->guest_count = guest_cnt;
366
367 cleanup:
368 mysql_free_result(rs);
369
370 return ret;
371 }
372
373 int user_login_count_load(MYSQL *db)
374 {
375 MYSQL_RES *rs = NULL;
376 MYSQL_ROW row;
377 char sql[SQL_BUFFER_LEN];
378
379 if (db == NULL)
380 {
381 log_error("NULL pointer error\n");
382 return -1;
383 }
384
385 snprintf(sql, sizeof(sql),
386 "SELECT ID FROM user_login_log ORDER BY ID DESC LIMIT 1");
387 if (mysql_query(db, sql) != 0)
388 {
389 log_error("Query user_login_log error: %s\n", mysql_error(db));
390 return -2;
391 }
392 if ((rs = mysql_store_result(db)) == NULL)
393 {
394 log_error("Get user_login_log data failed\n");
395 return -2;
396 }
397 if ((row = mysql_fetch_row(rs)))
398 {
399 p_user_list_pool->user_login_count = atoi(row[0]);
400 }
401 mysql_free_result(rs);
402
403 return 0;
404 }
405
406 int user_list_pool_init(const char *filename)
407 {
408 char filepath[FILE_PATH_LEN];
409 int fd;
410 size_t size;
411 void *p_shm;
412 #ifdef HAVE_SYSTEM_V
413 int proj_id;
414 key_t key;
415 int semid;
416 union semun arg;
417 #endif
418 int i;
419
420 if (p_user_list_pool != NULL || p_trie_action_dict != NULL)
421 {
422 log_error("p_user_list_pool already initialized\n");
423 return -1;
424 }
425
426 p_trie_action_dict = trie_dict_create();
427 if (p_trie_action_dict == NULL)
428 {
429 log_error("trie_dict_create() error\n");
430 return -1;
431 }
432
433 for (i = 0; i < user_action_map_size; i++)
434 {
435 if (trie_dict_set(p_trie_action_dict, user_action_map[i].name, (int64_t)(user_action_map[i].title)) < 0)
436 {
437 log_error("trie_dict_set(p_trie_action_dict, %s) error\n", user_action_map[i].name);
438 }
439 }
440
441 // Allocate shared memory
442 size = sizeof(USER_LIST_POOL);
443
444 strncpy(filepath, filename, sizeof(filepath) - 1);
445 filepath[sizeof(filepath) - 1] = '\0';
446 snprintf(user_list_shm_name, sizeof(user_list_shm_name), "/USER_LIST_SHM_%s", basename(filepath));
447
448 if (shm_unlink(user_list_shm_name) == -1 && errno != ENOENT)
449 {
450 log_error("shm_unlink(%s) error (%d)\n", user_list_shm_name, errno);
451 return -2;
452 }
453
454 if ((fd = shm_open(user_list_shm_name, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1)
455 {
456 log_error("shm_open(%s) error (%d)\n", user_list_shm_name, errno);
457 return -2;
458 }
459 if (ftruncate(fd, (off_t)size) == -1)
460 {
461 log_error("ftruncate(size=%d) error (%d)\n", size, errno);
462 close(fd);
463 return -2;
464 }
465
466 p_shm = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0L);
467 if (p_shm == MAP_FAILED)
468 {
469 log_error("mmap() error (%d)\n", errno);
470 close(fd);
471 return -2;
472 }
473
474 if (close(fd) < 0)
475 {
476 log_error("close(fd) error (%d)\n", errno);
477 return -1;
478 }
479
480 p_user_list_pool = p_shm;
481 p_user_list_pool->shm_size = size;
482
483 // Allocate semaphore as user list pool lock
484 #ifndef HAVE_SYSTEM_V
485 if (sem_init(&(p_user_list_pool->sem), 1, 1) == -1)
486 {
487 log_error("sem_init() error (%d)\n", errno);
488 return -3;
489 }
490
491 p_user_list_pool->read_lock_count = 0;
492 p_user_list_pool->write_lock_count = 0;
493 #else
494 proj_id = (int)(time(NULL) % getpid());
495 key = ftok(filename, proj_id);
496 if (key == -1)
497 {
498 log_error("ftok(%s %d) error (%d)\n", filename, proj_id, errno);
499 return -2;
500 }
501
502 size = 2; // r_sem and w_sem
503 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
504 if (semid == -1)
505 {
506 log_error("semget(user_list_pool_sem, size = %d) error (%d)\n", size, errno);
507 return -3;
508 }
509
510 // Initialize sem value to 0
511 arg.val = 0;
512 for (i = 0; i < size; i++)
513 {
514 if (semctl(semid, i, SETVAL, arg) == -1)
515 {
516 log_error("semctl(user_list_pool_sem, SETVAL) error (%d)\n", errno);
517 return -3;
518 }
519 }
520
521 p_user_list_pool->semid = semid;
522 #endif
523
524 // Set user counts to 0
525 p_user_list_pool->user_list[0].user_count = 0;
526 p_user_list_pool->user_list[1].user_count = 0;
527
528 p_user_list_pool->user_list_index_current = 0;
529 p_user_list_pool->user_list_index_new = 1;
530
531 p_user_list_pool->user_online_list_index_current = 0;
532 p_user_list_pool->user_online_list_index_new = 1;
533
534 user_stat_map_init(&(p_user_list_pool->user_stat_map));
535
536 return 0;
537 }
538
539 void user_list_pool_cleanup(void)
540 {
541 if (p_user_list_pool == NULL)
542 {
543 return;
544 }
545
546 #ifdef HAVE_SYSTEM_V
547 if (semctl(p_user_list_pool->semid, 0, IPC_RMID) == -1)
548 {
549 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_user_list_pool->semid, errno);
550 }
551 #else
552 if (sem_destroy(&(p_user_list_pool->sem)) == -1)
553 {
554 log_error("sem_destroy() error (%d)\n", errno);
555 }
556 #endif
557
558 detach_user_list_pool_shm();
559
560 if (shm_unlink(user_list_shm_name) == -1 && errno != ENOENT)
561 {
562 log_error("shm_unlink(%s) error (%d)\n", user_list_shm_name, errno);
563 }
564
565 user_list_shm_name[0] = '\0';
566
567 if (p_trie_action_dict != NULL)
568 {
569 trie_dict_destroy(p_trie_action_dict);
570
571 p_trie_action_dict = NULL;
572 }
573 }
574
575 int set_user_list_pool_shm_readonly(void)
576 {
577 if (p_user_list_pool != NULL && mprotect(p_user_list_pool, p_user_list_pool->shm_size, PROT_READ) < 0)
578 {
579 log_error("mprotect() error (%d)\n", errno);
580 return -1;
581 }
582
583 return 0;
584 }
585
586 int detach_user_list_pool_shm(void)
587 {
588 if (p_user_list_pool != NULL && munmap(p_user_list_pool, p_user_list_pool->shm_size) < 0)
589 {
590 log_error("munmap() error (%d)\n", errno);
591 return -1;
592 }
593
594 p_user_list_pool = NULL;
595
596 return 0;
597 }
598
599 int user_list_pool_reload(int online_user)
600 {
601 MYSQL *db = NULL;
602 int tmp;
603 int ret = 0;
604
605 if (p_user_list_pool == NULL)
606 {
607 log_error("p_user_list_pool not initialized\n");
608 return -1;
609 }
610
611 db = db_open();
612 if (db == NULL)
613 {
614 log_error("db_open() error: %s\n", mysql_error(db));
615 return -1;
616 }
617
618 if (online_user)
619 {
620 if (user_online_list_load(db, &(p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_new])) < 0)
621 {
622 log_error("user_online_list_load() error\n");
623 ret = -2;
624 goto cleanup;
625 }
626
627 if (user_login_count_load(db) < 0)
628 {
629 log_error("user_login_count_load() error\n");
630 ret = -2;
631 goto cleanup;
632 }
633 }
634 else
635 {
636 if (user_list_load(db, &(p_user_list_pool->user_list[p_user_list_pool->user_list_index_new])) < 0)
637 {
638 log_error("user_list_load() error\n");
639 ret = -2;
640 goto cleanup;
641 }
642 }
643
644 mysql_close(db);
645 db = NULL;
646
647 if (user_list_rw_lock() < 0)
648 {
649 log_error("user_list_rw_lock() error\n");
650 ret = -3;
651 goto cleanup;
652 }
653
654 if (online_user)
655 {
656 // Swap p_online_current and p_online_new
657 tmp = p_user_list_pool->user_online_list_index_current;
658 p_user_list_pool->user_online_list_index_current = p_user_list_pool->user_online_list_index_new;
659 p_user_list_pool->user_online_list_index_new = tmp;
660 }
661 else
662 {
663 // Swap index_current and index_new
664 tmp = p_user_list_pool->user_list_index_current;
665 p_user_list_pool->user_list_index_current = p_user_list_pool->user_list_index_new;
666 p_user_list_pool->user_list_index_new = tmp;
667 }
668
669 if (user_list_rw_unlock() < 0)
670 {
671 log_error("user_list_rw_unlock() error\n");
672 ret = -3;
673 goto cleanup;
674 }
675
676 cleanup:
677 mysql_close(db);
678
679 return ret;
680 }
681
682 int user_list_try_rd_lock(int wait_sec)
683 {
684 #ifdef HAVE_SYSTEM_V
685 struct sembuf sops[2];
686 #endif
687 struct timespec timeout;
688 int ret = 0;
689
690 if (p_user_list_pool == NULL)
691 {
692 log_error("p_user_list_pool not initialized\n");
693 return -1;
694 }
695
696 timeout.tv_sec = wait_sec;
697 timeout.tv_nsec = 0;
698
699 #ifdef HAVE_SYSTEM_V
700 sops[0].sem_num = 1; // w_sem
701 sops[0].sem_op = 0; // wait until unlocked
702 sops[0].sem_flg = 0;
703
704 sops[1].sem_num = 0; // r_sem
705 sops[1].sem_op = 1; // lock
706 sops[1].sem_flg = SEM_UNDO; // undo on terminate
707
708 ret = semtimedop(p_user_list_pool->semid, sops, 2, &timeout);
709 if (ret == -1 && errno != EAGAIN && errno != EINTR)
710 {
711 log_error("semop(lock read) error %d\n", errno);
712 }
713 #else
714 if (sem_timedwait(&(p_user_list_pool->sem), &timeout) == -1)
715 {
716 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
717 {
718 log_error("sem_timedwait() error %d\n", errno);
719 }
720 return -1;
721 }
722
723 if (p_user_list_pool->write_lock_count == 0)
724 {
725 p_user_list_pool->read_lock_count++;
726 }
727 else
728 {
729 errno = EAGAIN;
730 ret = -1;
731 }
732
733 if (sem_post(&(p_user_list_pool->sem)) == -1)
734 {
735 log_error("sem_post() error %d\n", errno);
736 return -1;
737 }
738 #endif
739
740 return ret;
741 }
742
743 int user_list_try_rw_lock(int wait_sec)
744 {
745 #ifdef HAVE_SYSTEM_V
746 struct sembuf sops[3];
747 #endif
748 struct timespec timeout;
749 int ret = 0;
750
751 if (p_user_list_pool == NULL)
752 {
753 log_error("p_user_list_pool not initialized\n");
754 return -1;
755 }
756
757 timeout.tv_sec = wait_sec;
758 timeout.tv_nsec = 0;
759
760 #ifdef HAVE_SYSTEM_V
761 sops[0].sem_num = 1; // w_sem
762 sops[0].sem_op = 0; // wait until unlocked
763 sops[0].sem_flg = 0;
764
765 sops[1].sem_num = 1; // w_sem
766 sops[1].sem_op = 1; // lock
767 sops[1].sem_flg = SEM_UNDO; // undo on terminate
768
769 sops[2].sem_num = 0; // r_sem
770 sops[2].sem_op = 0; // wait until unlocked
771 sops[2].sem_flg = 0;
772
773 ret = semtimedop(p_user_list_pool->semid, sops, 3, &timeout);
774 if (ret == -1 && errno != EAGAIN && errno != EINTR)
775 {
776 log_error("semop(lock write) error %d\n", errno);
777 }
778 #else
779 if (sem_timedwait(&(p_user_list_pool->sem), &timeout) == -1)
780 {
781 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
782 {
783 log_error("sem_timedwait() error %d\n", errno);
784 }
785 return -1;
786 }
787
788 if (p_user_list_pool->read_lock_count == 0 && p_user_list_pool->write_lock_count == 0)
789 {
790 p_user_list_pool->write_lock_count++;
791 }
792 else
793 {
794 errno = EAGAIN;
795 ret = -1;
796 }
797
798 if (sem_post(&(p_user_list_pool->sem)) == -1)
799 {
800 log_error("sem_post() error %d\n", errno);
801 return -1;
802 }
803 #endif
804
805 return ret;
806 }
807
808 int user_list_rd_unlock(void)
809 {
810 #ifdef HAVE_SYSTEM_V
811 struct sembuf sops[2];
812 #endif
813 int ret = 0;
814
815 if (p_user_list_pool == NULL)
816 {
817 log_error("p_user_list_pool not initialized\n");
818 return -1;
819 }
820
821 #ifdef HAVE_SYSTEM_V
822 sops[0].sem_num = 0; // r_sem
823 sops[0].sem_op = -1; // unlock
824 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
825
826 ret = semop(p_user_list_pool->semid, sops, 1);
827 if (ret == -1 && errno != EAGAIN && errno != EINTR)
828 {
829 log_error("semop(unlock read) error %d\n", errno);
830 }
831 #else
832 if (sem_wait(&(p_user_list_pool->sem)) == -1)
833 {
834 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
835 {
836 log_error("sem_wait() error %d\n", errno);
837 }
838 return -1;
839 }
840
841 if (p_user_list_pool->read_lock_count > 0)
842 {
843 p_user_list_pool->read_lock_count--;
844 }
845 else
846 {
847 log_error("read_lock_count already 0\n");
848 }
849
850 if (sem_post(&(p_user_list_pool->sem)) == -1)
851 {
852 log_error("sem_post() error %d\n", errno);
853 return -1;
854 }
855 #endif
856
857 return ret;
858 }
859
860 int user_list_rw_unlock(void)
861 {
862 #ifdef HAVE_SYSTEM_V
863 struct sembuf sops[1];
864 #endif
865 int ret = 0;
866
867 if (p_user_list_pool == NULL)
868 {
869 log_error("p_user_list_pool not initialized\n");
870 return -1;
871 }
872
873 #ifdef HAVE_SYSTEM_V
874 sops[0].sem_num = 1; // w_sem
875 sops[0].sem_op = -1; // unlock
876 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
877
878 ret = semop(p_user_list_pool->semid, sops, 1);
879 if (ret == -1 && errno != EAGAIN && errno != EINTR)
880 {
881 log_error("semop(unlock write) error %d\n", errno);
882 }
883 #else
884 if (sem_wait(&(p_user_list_pool->sem)) == -1)
885 {
886 if (errno != ETIMEDOUT && errno != EAGAIN && errno != EINTR)
887 {
888 log_error("sem_wait() error %d\n", errno);
889 }
890 return -1;
891 }
892
893 if (p_user_list_pool->write_lock_count > 0)
894 {
895 p_user_list_pool->write_lock_count--;
896 }
897 else
898 {
899 log_error("write_lock_count already 0\n");
900 }
901
902 if (sem_post(&(p_user_list_pool->sem)) == -1)
903 {
904 log_error("sem_post() error %d\n", errno);
905 return -1;
906 }
907 #endif
908
909 return ret;
910 }
911
912 int user_list_rd_lock(void)
913 {
914 int timer = 0;
915 int ret = -1;
916 time_t tm_first_failure = 0;
917
918 if (p_user_list_pool == NULL)
919 {
920 log_error("p_user_list_pool not initialized\n");
921 return -1;
922 }
923
924 while (!SYS_server_exit)
925 {
926 ret = user_list_try_rd_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
927 if (ret == 0) // success
928 {
929 break;
930 }
931 else if (errno == EAGAIN || errno == EINTR) // retry
932 {
933 timer++;
934 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
935 {
936 log_error("user_list_try_rd_lock() tried %d times\n", timer);
937
938 if (time(NULL) - tm_first_failure >= USER_LIST_DEAD_LOCK_TIMEOUT)
939 {
940 log_error("Unable to acquire rw_lock for %d seconds\n", time(NULL) - tm_first_failure);
941 #ifndef HAVE_SYSTEM_V
942 user_list_reset_lock();
943 log_error("Reset POSIX semaphore to resolve dead lock\n");
944 #endif
945 break;
946 }
947 }
948 usleep(100 * 1000); // 0.1 second
949 }
950 else // failed
951 {
952 log_error("user_list_try_rd_lock() failed\n");
953 break;
954 }
955 }
956
957 return ret;
958 }
959
960 int user_list_rw_lock(void)
961 {
962 int timer = 0;
963 int ret = -1;
964 time_t tm_first_failure = 0;
965
966 if (p_user_list_pool == NULL)
967 {
968 log_error("p_user_list_pool not initialized\n");
969 return -1;
970 }
971
972 while (!SYS_server_exit)
973 {
974 ret = user_list_try_rw_lock(USER_LIST_TRY_LOCK_WAIT_TIME);
975 if (ret == 0) // success
976 {
977 break;
978 }
979 else if (errno == EAGAIN || errno == EINTR) // retry
980 {
981 timer++;
982 if (timer % USER_LIST_TRY_LOCK_TIMES == 0)
983 {
984 log_error("user_list_try_rw_lock() tried %d times\n", timer);
985
986 if (time(NULL) - tm_first_failure >= USER_LIST_DEAD_LOCK_TIMEOUT)
987 {
988 log_error("Unable to acquire rw_lock for %d seconds\n", time(NULL) - tm_first_failure);
989 #ifndef HAVE_SYSTEM_V
990 user_list_reset_lock();
991 log_error("Reset POSIX semaphore to resolve dead lock\n");
992 #endif
993 break;
994 }
995 }
996 usleep(100 * 1000); // 0.1 second
997 }
998 else // failed
999 {
1000 log_error("user_list_try_rw_lock() failed\n");
1001 break;
1002 }
1003 }
1004
1005 return ret;
1006 }
1007
1008 #ifndef HAVE_SYSTEM_V
1009 int user_list_reset_lock(void)
1010 {
1011 if (p_user_list_pool == NULL)
1012 {
1013 log_error("p_user_list_pool not initialized\n");
1014 return -1;
1015 }
1016
1017 if (sem_destroy(&(p_user_list_pool->sem)) == -1)
1018 {
1019 log_error("sem_destroy() error (%d)\n", errno);
1020 }
1021
1022 p_user_list_pool->read_lock_count = 0;
1023 p_user_list_pool->write_lock_count = 0;
1024
1025 if (sem_init(&(p_user_list_pool->sem), 1, 1) == -1)
1026 {
1027 log_error("sem_init() error (%d)\n", errno);
1028 }
1029
1030 return 0;
1031 }
1032 #endif
1033
1034 int query_user_list(int page_id, USER_INFO *p_users, int *p_user_count, int *p_page_count)
1035 {
1036 int ret = 0;
1037
1038 if (p_users == NULL || p_user_count == NULL || p_page_count == NULL)
1039 {
1040 log_error("NULL pointer error\n");
1041 return -1;
1042 }
1043
1044 *p_user_count = 0;
1045 *p_page_count = 0;
1046
1047 // acquire lock of user list
1048 if (user_list_rd_lock() < 0)
1049 {
1050 log_error("user_list_rd_lock() error\n");
1051 return -2;
1052 }
1053
1054 if (p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count == 0)
1055 {
1056 // empty list
1057 ret = 0;
1058 goto cleanup;
1059 }
1060
1061 *p_page_count = (p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count + BBS_user_limit_per_page - 1) /
1062 BBS_user_limit_per_page;
1063
1064 if (page_id < 0 || page_id >= *p_page_count)
1065 {
1066 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
1067 ret = -3;
1068 goto cleanup;
1069 }
1070
1071 *p_user_count = MIN(BBS_user_limit_per_page,
1072 p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count -
1073 page_id * BBS_user_limit_per_page);
1074
1075 memcpy(p_users,
1076 p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users + page_id * BBS_user_limit_per_page,
1077 sizeof(USER_INFO) * (size_t)(*p_user_count));
1078
1079 cleanup:
1080 // release lock of user list
1081 if (user_list_rd_unlock() < 0)
1082 {
1083 log_error("user_list_rd_unlock() error\n");
1084 ret = -1;
1085 }
1086
1087 return ret;
1088 }
1089
1090 int query_user_online_list(int page_id, USER_ONLINE_INFO *p_online_users, int *p_user_count, int *p_page_count)
1091 {
1092 int ret = 0;
1093
1094 if (p_online_users == NULL || p_user_count == NULL || p_page_count == NULL)
1095 {
1096 log_error("NULL pointer error\n");
1097 return -1;
1098 }
1099
1100 *p_user_count = 0;
1101 *p_page_count = 0;
1102
1103 // acquire lock of user list
1104 if (user_list_rd_lock() < 0)
1105 {
1106 log_error("user_list_rd_lock() error\n");
1107 return -2;
1108 }
1109
1110 if (p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count == 0)
1111 {
1112 // empty list
1113 ret = 0;
1114 goto cleanup;
1115 }
1116
1117 *p_page_count = (p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count + BBS_user_limit_per_page - 1) / BBS_user_limit_per_page;
1118
1119 if (page_id < 0 || page_id >= *p_page_count)
1120 {
1121 log_error("Invalid page_id = %d, not in range [0, %d)\n", page_id, *p_page_count);
1122 ret = -3;
1123 goto cleanup;
1124 }
1125
1126 *p_user_count = MIN(BBS_user_limit_per_page,
1127 p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count -
1128 page_id * BBS_user_limit_per_page);
1129
1130 memcpy(p_online_users,
1131 p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users + page_id * BBS_user_limit_per_page,
1132 sizeof(USER_ONLINE_INFO) * (size_t)(*p_user_count));
1133
1134 cleanup:
1135 // release lock of user list
1136 if (user_list_rd_unlock() < 0)
1137 {
1138 log_error("user_list_rd_unlock() error\n");
1139 ret = -1;
1140 }
1141
1142 return ret;
1143 }
1144
1145 int get_user_list_count(int *p_user_cnt)
1146 {
1147 if (p_user_cnt == NULL)
1148 {
1149 log_error("NULL pointer error\n");
1150 return -1;
1151 }
1152
1153 // acquire lock of user list
1154 if (user_list_rd_lock() < 0)
1155 {
1156 log_error("user_list_rd_lock() error\n");
1157 return -2;
1158 }
1159
1160 *p_user_cnt = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count;
1161
1162 // release lock of user list
1163 if (user_list_rd_unlock() < 0)
1164 {
1165 log_error("user_list_rd_unlock() error\n");
1166 return -2;
1167 }
1168
1169 return 0;
1170 }
1171
1172 int get_user_online_list_count(int *p_user_cnt, int *p_guest_cnt)
1173 {
1174 if (p_user_cnt == NULL || p_guest_cnt == NULL)
1175 {
1176 log_error("NULL pointer error\n");
1177 return -1;
1178 }
1179
1180 // acquire lock of user list
1181 if (user_list_rd_lock() < 0)
1182 {
1183 log_error("user_list_rd_lock() error\n");
1184 return -2;
1185 }
1186
1187 *p_user_cnt = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count;
1188 *p_guest_cnt = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].guest_count;
1189
1190 // release lock of user list
1191 if (user_list_rd_unlock() < 0)
1192 {
1193 log_error("user_list_rd_unlock() error\n");
1194 return -2;
1195 }
1196
1197 return 0;
1198 }
1199
1200 int get_user_login_count(int *p_login_cnt)
1201 {
1202 if (p_login_cnt == NULL)
1203 {
1204 log_error("NULL pointer error\n");
1205 return -1;
1206 }
1207
1208 *p_login_cnt = p_user_list_pool->user_login_count;
1209
1210 return 0;
1211 }
1212
1213 int query_user_info(int32_t id, USER_INFO *p_user)
1214 {
1215 int ret = 0;
1216
1217 if (p_user == NULL)
1218 {
1219 log_error("NULL pointer error\n");
1220 return -1;
1221 }
1222
1223 // acquire lock of user list
1224 if (user_list_rd_lock() < 0)
1225 {
1226 log_error("user_list_rd_lock() error\n");
1227 return -2;
1228 }
1229
1230 if (id >= 0 && id < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count) // Found
1231 {
1232 *p_user = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id];
1233 ret = 1;
1234 }
1235
1236 // release lock of user list
1237 if (user_list_rd_unlock() < 0)
1238 {
1239 log_error("user_list_rd_unlock() error\n");
1240 ret = -1;
1241 }
1242
1243 return ret;
1244 }
1245
1246 int query_user_info_by_uid(int32_t uid, USER_INFO *p_user, char *p_intro_buf, size_t intro_buf_len)
1247 {
1248 int left;
1249 int right;
1250 int mid;
1251 int32_t id;
1252 int ret = 0;
1253
1254 if (p_user == NULL)
1255 {
1256 log_error("NULL pointer error\n");
1257 return -1;
1258 }
1259
1260 // acquire lock of user list
1261 if (user_list_rd_lock() < 0)
1262 {
1263 log_error("user_list_rd_lock() error\n");
1264 return -2;
1265 }
1266
1267 left = 0;
1268 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1269
1270 while (left < right)
1271 {
1272 mid = (left + right) / 2;
1273 if (uid < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1274 {
1275 right = mid - 1;
1276 }
1277 else if (uid > p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1278 {
1279 left = mid + 1;
1280 }
1281 else // if (uid == p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1282 {
1283 left = mid;
1284 break;
1285 }
1286 }
1287
1288 if (uid == p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left].uid) // Found
1289 {
1290 id = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left].id;
1291 *p_user = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id];
1292 ret = 1;
1293
1294 if (p_intro_buf != NULL)
1295 {
1296 strncpy(p_intro_buf, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[id].intro, intro_buf_len - 1);
1297 p_intro_buf[intro_buf_len - 1] = '\0';
1298 p_user->intro = p_intro_buf;
1299 }
1300 }
1301
1302 // release lock of user list
1303 if (user_list_rd_unlock() < 0)
1304 {
1305 log_error("user_list_rd_unlock() error\n");
1306 ret = -1;
1307 }
1308
1309 return ret;
1310 }
1311
1312 int query_user_info_by_username(const char *username_prefix, int max_user_cnt,
1313 int32_t uid_list[], char username_list[][BBS_username_max_len + 1])
1314 {
1315 int left;
1316 int right;
1317 int mid;
1318 int left_save;
1319 int ret = 0;
1320 size_t prefix_len;
1321 int comp;
1322 int i;
1323
1324 if (username_prefix == NULL || uid_list == NULL || username_list == NULL)
1325 {
1326 log_error("NULL pointer error\n");
1327 return -1;
1328 }
1329
1330 prefix_len = strlen(username_prefix);
1331
1332 // acquire lock of user list
1333 if (user_list_rd_lock() < 0)
1334 {
1335 log_error("user_list_rd_lock() error\n");
1336 return -2;
1337 }
1338
1339 left = 0;
1340 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1341
1342 while (left < right)
1343 {
1344 mid = (left + right) / 2;
1345 comp = strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[mid].username, prefix_len);
1346 if (comp < 0)
1347 {
1348 right = mid - 1;
1349 }
1350 else if (comp > 0)
1351 {
1352 left = mid + 1;
1353 }
1354 else // if (comp == 0)
1355 {
1356 left = mid;
1357 break;
1358 }
1359 }
1360
1361 if (strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[left].username, prefix_len) == 0) // Found
1362 {
1363 #ifdef _DEBUG
1364 log_error("Debug: match found, pos=%d\n", left);
1365 #endif
1366
1367 left_save = left;
1368 right = left;
1369 left = 0;
1370
1371 while (left < right)
1372 {
1373 mid = (left + right) / 2;
1374 comp = strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[mid].username, prefix_len);
1375 if (comp > 0)
1376 {
1377 left = mid + 1;
1378 }
1379 else if (comp == 0)
1380 {
1381 right = mid;
1382 }
1383 else // if (comp < 0)
1384 {
1385 log_error("Bug: left=%d right=%d mid=%d");
1386 ret = -2;
1387 goto cleanup;
1388 }
1389 }
1390
1391 #ifdef _DEBUG
1392 log_error("Debug: first match found, pos=%d\n", right);
1393 #endif
1394
1395 left = left_save;
1396 left_save = right;
1397 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1398
1399 while (left < right)
1400 {
1401 mid = (left + right) / 2 + (left + right) % 2;
1402 comp = strncasecmp(username_prefix, p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[mid].username, prefix_len);
1403 if (comp < 0)
1404 {
1405 right = mid - 1;
1406 }
1407 else if (comp == 0)
1408 {
1409 left = mid;
1410 }
1411 else // if (comp > 0)
1412 {
1413 log_error("Bug: left=%d right=%d mid=%d");
1414 ret = -2;
1415 goto cleanup;
1416 }
1417 }
1418
1419 #ifdef _DEBUG
1420 log_error("Debug: last match found, pos=%d\n", left);
1421 #endif
1422
1423 right = left;
1424 left = left_save;
1425
1426 for (i = 0; i < max_user_cnt && left + i <= right; i++)
1427 {
1428 uid_list[i] = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[left + i].uid;
1429 strncpy(username_list[i], p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].users[left + i].username, sizeof(username_list[i]) - 1);
1430 username_list[i][sizeof(username_list[i]) - 1] = '\0';
1431 }
1432 ret = i;
1433 }
1434
1435 cleanup:
1436 // release lock of user list
1437 if (user_list_rd_unlock() < 0)
1438 {
1439 log_error("user_list_rd_unlock() error\n");
1440 ret = -1;
1441 }
1442
1443 return ret;
1444 }
1445
1446 int query_user_online_info(int32_t id, USER_ONLINE_INFO *p_user)
1447 {
1448 int ret = 0;
1449
1450 if (p_user == NULL)
1451 {
1452 log_error("NULL pointer error\n");
1453 return -1;
1454 }
1455
1456 // acquire lock of user list
1457 if (user_list_rd_lock() < 0)
1458 {
1459 log_error("user_list_rd_lock() error\n");
1460 return -2;
1461 }
1462
1463 if (id >= 0 && id < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count) // Found
1464 {
1465 *p_user = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users[id];
1466 ret = 1;
1467 }
1468
1469 // release lock of user list
1470 if (user_list_rd_unlock() < 0)
1471 {
1472 log_error("user_list_rd_unlock() error\n");
1473 ret = -1;
1474 }
1475
1476 return ret;
1477 }
1478
1479 int query_user_online_info_by_uid(int32_t uid, USER_ONLINE_INFO *p_users, int *p_user_cnt, int start_id)
1480 {
1481 int left;
1482 int right;
1483 int mid;
1484 int32_t id;
1485 int ret = 0;
1486 int i;
1487 int user_cnt;
1488
1489 if (p_users == NULL || p_user_cnt == NULL)
1490 {
1491 log_error("NULL pointer error\n");
1492 return -1;
1493 }
1494
1495 user_cnt = *p_user_cnt;
1496 *p_user_cnt = 0;
1497
1498 // acquire lock of user list
1499 if (user_list_rd_lock() < 0)
1500 {
1501 log_error("user_list_rd_lock() error\n");
1502 return -2;
1503 }
1504
1505 left = start_id;
1506 right = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count - 1;
1507
1508 while (left < right)
1509 {
1510 mid = (left + right) / 2;
1511 if (uid < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1512 {
1513 right = mid - 1;
1514 }
1515 else if (uid > p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1516 {
1517 left = mid + 1;
1518 }
1519 else // if (uid == p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1520 {
1521 left = mid;
1522 break;
1523 }
1524 }
1525
1526 if (uid == p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[left].uid)
1527 {
1528 right = left;
1529 left = start_id;
1530
1531 while (left < right)
1532 {
1533 mid = (left + right) / 2;
1534 if (uid - 1 < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1535 {
1536 right = mid;
1537 }
1538 else // if (uid - 1 >= p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[mid].uid)
1539 {
1540 left = mid + 1;
1541 }
1542 }
1543
1544 for (i = 0;
1545 left < p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].user_count && i < user_cnt &&
1546 uid == p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[left].uid;
1547 left++, i++)
1548 {
1549 id = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].index_uid[left].id;
1550 p_users[i] = p_user_list_pool->user_online_list[p_user_list_pool->user_online_list_index_current].users[id];
1551 }
1552
1553 if (i > 0)
1554 {
1555 *p_user_cnt = i;
1556 ret = 1;
1557 }
1558 }
1559
1560 // release lock of user list
1561 if (user_list_rd_unlock() < 0)
1562 {
1563 log_error("user_list_rd_unlock() error\n");
1564 ret = -1;
1565 }
1566
1567 return ret;
1568 }
1569
1570 int get_user_id_list(int32_t *p_uid_list, int *p_user_cnt, int start_uid)
1571 {
1572 int left;
1573 int right;
1574 int mid;
1575 int ret = 0;
1576 int i;
1577
1578 if (p_uid_list == NULL || p_user_cnt == NULL)
1579 {
1580 log_error("NULL pointer error\n");
1581 return -1;
1582 }
1583
1584 // acquire lock of user list
1585 if (user_list_rd_lock() < 0)
1586 {
1587 log_error("user_list_rd_lock() error\n");
1588 return -2;
1589 }
1590
1591 left = 0;
1592 right = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count - 1;
1593
1594 while (left < right)
1595 {
1596 mid = (left + right) / 2;
1597 if (start_uid < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1598 {
1599 right = mid - 1;
1600 }
1601 else if (start_uid > p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1602 {
1603 left = mid + 1;
1604 }
1605 else // if (start_uid == p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[mid].uid)
1606 {
1607 left = mid;
1608 break;
1609 }
1610 }
1611
1612 for (i = 0; i < *p_user_cnt && left + i < p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].user_count; i++)
1613 {
1614 p_uid_list[i] = p_user_list_pool->user_list[p_user_list_pool->user_list_index_current].index_uid[left + i].uid;
1615 }
1616 *p_user_cnt = i;
1617
1618 // release lock of user list
1619 if (user_list_rd_unlock() < 0)
1620 {
1621 log_error("user_list_rd_unlock() error\n");
1622 ret = -1;
1623 }
1624
1625 return ret;
1626 }
1627
1628 int user_stat_update(void)
1629 {
1630 return user_stat_map_update(&(p_user_list_pool->user_stat_map));
1631 }
1632
1633 int user_article_cnt_inc(int32_t uid, int n)
1634 {
1635 return user_stat_article_cnt_inc(&(p_user_list_pool->user_stat_map), uid, n);
1636 }
1637
1638 int get_user_article_cnt(int32_t uid)
1639 {
1640 const USER_STAT *p_stat;
1641 int ret;
1642
1643 ret = user_stat_get(&(p_user_list_pool->user_stat_map), uid, &p_stat);
1644 if (ret < 0)
1645 {
1646 log_error("user_stat_get(uid=%d) error: %d\n", uid, ret);
1647 return -1;
1648 }
1649 else if (ret == 0) // user not found
1650 {
1651 return -1;
1652 }
1653
1654 return p_stat->article_count;
1655 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1