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

Contents of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.46 - (show annotations)
Thu Oct 23 05:54:16 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.45: +16 -0 lines
Content type: text/x-csrc
Update user_stat_article_cnt in section_list_set_article_visible()

1 /***************************************************************************
2 section_list.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "log.h"
18 #include "section_list.h"
19 #include "trie_dict.h"
20 #include "user_list.h"
21 #include <errno.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/ipc.h>
28 #include <sys/param.h>
29 #include <sys/sem.h>
30 #include <sys/shm.h>
31
32 #ifdef _SEM_SEMUN_UNDEFINED
33 union semun
34 {
35 int val; /* Value for SETVAL */
36 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
37 unsigned short *array; /* Array for GETALL, SETALL */
38 struct seminfo *__buf; /* Buffer for IPC_INFO
39 (Linux-specific) */
40 };
41 #endif // #ifdef _SEM_SEMUN_UNDEFINED
42
43 #define SECTION_TRY_LOCK_WAIT_TIME 1 // second
44 #define SECTION_TRY_LOCK_TIMES 10
45
46 #define ARTICLE_BLOCK_PER_SHM 1000 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
47 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 80 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
48 #define ARTICLE_BLOCK_PER_POOL (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)
49
50 #define CALCULATE_PAGE_THRESHOLD 100 // Adjust to tune performance of moving topic between sections
51
52 #define SID_STR_LEN 5 // 32-bit + NULL
53
54 struct article_block_t
55 {
56 ARTICLE articles[ARTICLE_PER_BLOCK];
57 int article_count;
58 struct article_block_t *p_next_block;
59 };
60 typedef struct article_block_t ARTICLE_BLOCK;
61
62 struct article_block_pool_t
63 {
64 int shmid;
65 struct
66 {
67 int shmid;
68 void *p_shm;
69 } shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
70 int shm_count;
71 ARTICLE_BLOCK *p_block_free_list;
72 ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
73 int block_count;
74 };
75 typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
76
77 static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
78
79 SECTION_LIST_POOL *p_section_list_pool = NULL;
80
81 int article_block_init(const char *filename, int block_count)
82 {
83 int shmid;
84 int proj_id;
85 key_t key;
86 size_t size;
87 void *p_shm;
88 int i;
89 int block_count_in_shm;
90 ARTICLE_BLOCK *p_block_in_shm;
91 ARTICLE_BLOCK **pp_block_next;
92
93 if (p_article_block_pool != NULL)
94 {
95 log_error("article_block_pool already initialized\n");
96 return -1;
97 }
98
99 if (block_count <= 0 || block_count > ARTICLE_BLOCK_PER_POOL)
100 {
101 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
102 return -2;
103 }
104
105 // Allocate shared memory
106 proj_id = ARTICLE_BLOCK_SHM_COUNT_LIMIT; // keep different from proj_id used to create block shm
107 key = ftok(filename, proj_id);
108 if (key == -1)
109 {
110 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
111 return -3;
112 }
113
114 size = sizeof(ARTICLE_BLOCK_POOL);
115 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
116 if (shmid == -1)
117 {
118 log_error("shmget(article_block_pool_shm, size = %d) error (%d)\n", size, errno);
119 return -3;
120 }
121 p_shm = shmat(shmid, NULL, 0);
122 if (p_shm == (void *)-1)
123 {
124 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
125 return -3;
126 }
127
128 p_article_block_pool = p_shm;
129 p_article_block_pool->shmid = shmid;
130
131 p_article_block_pool->shm_count = 0;
132 pp_block_next = &(p_article_block_pool->p_block_free_list);
133
134 while (block_count > 0)
135 {
136 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
137 block_count -= block_count_in_shm;
138
139 proj_id = p_article_block_pool->shm_count;
140 key = ftok(filename, proj_id);
141 if (key == -1)
142 {
143 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
144 article_block_cleanup();
145 return -3;
146 }
147
148 size = sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
149 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
150 if (shmid == -1)
151 {
152 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
153 article_block_cleanup();
154 return -3;
155 }
156 p_shm = shmat(shmid, NULL, 0);
157 if (p_shm == (void *)-1)
158 {
159 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
160 article_block_cleanup();
161 return -3;
162 }
163
164 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->shmid = shmid;
165 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->p_shm = p_shm;
166 p_article_block_pool->shm_count++;
167
168 p_block_in_shm = p_shm;
169 *pp_block_next = p_block_in_shm;
170
171 for (i = 0; i < block_count_in_shm; i++)
172 {
173 if (i < block_count_in_shm - 1)
174 {
175 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
176 }
177 else
178 {
179 (p_block_in_shm + i)->p_next_block = NULL;
180 pp_block_next = &((p_block_in_shm + i)->p_next_block);
181 }
182 }
183 }
184
185 p_article_block_pool->block_count = 0;
186
187 return 0;
188 }
189
190 void article_block_cleanup(void)
191 {
192 int shmid;
193
194 if (p_article_block_pool == NULL)
195 {
196 return;
197 }
198
199 for (int i = 0; i < p_article_block_pool->shm_count; i++)
200 {
201 if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
202 {
203 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
204 }
205
206 if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
207 {
208 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
209 }
210 }
211
212 shmid = p_article_block_pool->shmid;
213
214 if (shmdt(p_article_block_pool) == -1)
215 {
216 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
217 }
218
219 if (shmctl(shmid, IPC_RMID, NULL) == -1)
220 {
221 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
222 }
223
224 p_article_block_pool = NULL;
225 }
226
227 int set_article_block_shm_readonly(void)
228 {
229 int shmid;
230 void *p_shm;
231 int i;
232
233 if (p_article_block_pool == NULL)
234 {
235 log_error("article_block_pool not initialized\n");
236 return -1;
237 }
238
239 for (i = 0; i < p_article_block_pool->shm_count; i++)
240 {
241 shmid = (p_article_block_pool->shm_pool + i)->shmid;
242
243 // Remap shared memory in read-only mode
244 p_shm = shmat(shmid, (p_article_block_pool->shm_pool + i)->p_shm, SHM_RDONLY | SHM_REMAP);
245 if (p_shm == (void *)-1)
246 {
247 log_error("shmat(article_block_pool shmid = %d) error (%d)\n", shmid, errno);
248 return -2;
249 }
250 }
251
252 return 0;
253 }
254
255 int detach_article_block_shm(void)
256 {
257 int shmid;
258
259 if (p_article_block_pool == NULL)
260 {
261 return -1;
262 }
263
264 for (int i = 0; i < p_article_block_pool->shm_count; i++)
265 {
266 if ((p_article_block_pool->shm_pool + i)->p_shm != NULL && shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
267 {
268 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
269 return -2;
270 }
271 }
272
273 shmid = p_article_block_pool->shmid;
274
275 if (shmdt(p_article_block_pool) == -1)
276 {
277 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
278 return -3;
279 }
280
281 p_article_block_pool = NULL;
282
283 return 0;
284 }
285
286 inline static ARTICLE_BLOCK *pop_free_article_block(void)
287 {
288 ARTICLE_BLOCK *p_block = NULL;
289
290 if (p_article_block_pool->p_block_free_list != NULL)
291 {
292 p_block = p_article_block_pool->p_block_free_list;
293 p_article_block_pool->p_block_free_list = p_block->p_next_block;
294 p_block->p_next_block = NULL;
295 p_block->article_count = 0;
296 }
297
298 return p_block;
299 }
300
301 inline static void push_free_article_block(ARTICLE_BLOCK *p_block)
302 {
303 p_block->p_next_block = p_article_block_pool->p_block_free_list;
304 p_article_block_pool->p_block_free_list = p_block;
305 }
306
307 int article_block_reset(void)
308 {
309 ARTICLE_BLOCK *p_block;
310
311 if (p_article_block_pool == NULL)
312 {
313 log_error("article_block_pool not initialized\n");
314 return -1;
315 }
316
317 while (p_article_block_pool->block_count > 0)
318 {
319 p_article_block_pool->block_count--;
320 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count];
321 push_free_article_block(p_block);
322 }
323
324 return 0;
325 }
326
327 ARTICLE *article_block_find_by_aid(int32_t aid)
328 {
329 ARTICLE_BLOCK *p_block;
330 int left;
331 int right;
332 int mid;
333
334 if (p_article_block_pool == NULL)
335 {
336 log_error("article_block_pool not initialized\n");
337 return NULL;
338 }
339
340 if (p_article_block_pool->block_count == 0) // empty
341 {
342 return NULL;
343 }
344
345 left = 0;
346 right = p_article_block_pool->block_count;
347
348 // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
349 while (left < right - 1)
350 {
351 // get block offset no less than mid value of left and right block offsets
352 mid = (left + right) / 2 + (right - left) % 2;
353
354 if (mid >= p_article_block_pool->block_count)
355 {
356 log_error("block(mid = %d) is out of boundary\n", mid);
357 return NULL;
358 }
359
360 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
361 {
362 right = mid;
363 }
364 else
365 {
366 left = mid;
367 }
368 }
369
370 p_block = p_article_block_pool->p_block[left];
371
372 left = 0;
373 right = p_block->article_count - 1;
374
375 // aid in the range [ aid of articles[left], aid of articles[right] ]
376 while (left < right)
377 {
378 mid = (left + right) / 2;
379
380 if (aid <= p_block->articles[mid].aid)
381 {
382 right = mid;
383 }
384 else
385 {
386 left = mid + 1;
387 }
388 }
389
390 if (aid != p_block->articles[left].aid) // not found
391 {
392 return NULL;
393 }
394
395 return (p_block->articles + left); // found
396 }
397
398 ARTICLE *article_block_find_by_index(int index)
399 {
400 ARTICLE_BLOCK *p_block;
401
402 if (p_article_block_pool == NULL)
403 {
404 log_error("article_block_pool not initialized\n");
405 return NULL;
406 }
407
408 if (index < 0 || index / ARTICLE_PER_BLOCK >= p_article_block_pool->block_count)
409 {
410 log_error("article_block_find_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
411 return NULL;
412 }
413
414 p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];
415
416 if (index % ARTICLE_PER_BLOCK >= p_block->article_count)
417 {
418 log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
419 return NULL;
420 }
421
422 return (p_block->articles + (index % ARTICLE_PER_BLOCK));
423 }
424
425 extern int section_list_init(const char *filename)
426 {
427 int semid;
428 int shmid;
429 int proj_id;
430 key_t key;
431 size_t size;
432 void *p_shm;
433 union semun arg;
434 int i;
435
436 if (p_section_list_pool != NULL)
437 {
438 section_list_cleanup();
439 }
440
441 proj_id = (int)(time(NULL) % getpid());
442 key = ftok(filename, proj_id);
443 if (key == -1)
444 {
445 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
446 return -3;
447 }
448
449 // Allocate shared memory
450 size = sizeof(SECTION_LIST_POOL);
451 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
452 if (shmid == -1)
453 {
454 log_error("shmget(section_list_pool_shm, size = %d) error (%d)\n", size, errno);
455 return -3;
456 }
457 p_shm = shmat(shmid, NULL, 0);
458 if (p_shm == (void *)-1)
459 {
460 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
461 return -3;
462 }
463
464 p_section_list_pool = p_shm;
465 p_section_list_pool->shmid = shmid;
466 p_section_list_pool->section_count = 0;
467
468 // Allocate semaphore as section locks
469 size = 2 * (BBS_max_section + 1); // r_sem and w_sem per section, the last pair for all sections
470 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
471 if (semid == -1)
472 {
473 log_error("semget(section_list_pool_sem, size = %d) error (%d)\n", size, errno);
474 return -3;
475 }
476
477 // Initialize sem value to 0
478 arg.val = 0;
479 for (i = 0; i < size; i++)
480 {
481 if (semctl(semid, i, SETVAL, arg) == -1)
482 {
483 log_error("semctl(section_list_pool_sem, SETVAL) error (%d)\n", errno);
484 return -3;
485 }
486 }
487
488 p_section_list_pool->semid = semid;
489
490 p_section_list_pool->p_trie_dict_section_by_name = trie_dict_create();
491 if (p_section_list_pool->p_trie_dict_section_by_name == NULL)
492 {
493 log_error("trie_dict_create() OOM\n", BBS_max_section);
494 return -2;
495 }
496
497 p_section_list_pool->p_trie_dict_section_by_sid = trie_dict_create();
498 if (p_section_list_pool->p_trie_dict_section_by_sid == NULL)
499 {
500 log_error("trie_dict_create() OOM\n", BBS_max_section);
501 return -2;
502 }
503
504 return 0;
505 }
506
507 void section_list_cleanup(void)
508 {
509 int shmid;
510
511 if (p_section_list_pool == NULL)
512 {
513 return;
514 }
515
516 if (p_section_list_pool->p_trie_dict_section_by_name != NULL)
517 {
518 trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_name);
519 p_section_list_pool->p_trie_dict_section_by_name = NULL;
520 }
521
522 if (p_section_list_pool->p_trie_dict_section_by_sid != NULL)
523 {
524 trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_sid);
525 p_section_list_pool->p_trie_dict_section_by_sid = NULL;
526 }
527
528 shmid = p_section_list_pool->shmid;
529
530 if (semctl(p_section_list_pool->semid, 0, IPC_RMID) == -1)
531 {
532 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_section_list_pool->semid, errno);
533 }
534
535 if (shmdt(p_section_list_pool) == -1)
536 {
537 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
538 }
539
540 if (shmctl(shmid, IPC_RMID, NULL) == -1)
541 {
542 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
543 }
544
545 p_section_list_pool = NULL;
546 }
547
548 int set_section_list_shm_readonly(void)
549 {
550 int shmid;
551 void *p_shm;
552
553 if (p_section_list_pool == NULL)
554 {
555 log_error("p_section_list_pool not initialized\n");
556 return -1;
557 }
558
559 shmid = p_section_list_pool->shmid;
560
561 // Remap shared memory in read-only mode
562 p_shm = shmat(shmid, p_section_list_pool, SHM_RDONLY | SHM_REMAP);
563 if (p_shm == (void *)-1)
564 {
565 log_error("shmat(section_list_pool shmid = %d) error (%d)\n", shmid, errno);
566 return -3;
567 }
568
569 p_section_list_pool = p_shm;
570
571 return 0;
572 }
573
574 int detach_section_list_shm(void)
575 {
576 if (p_section_list_pool != NULL && shmdt(p_section_list_pool) == -1)
577 {
578 log_error("shmdt(section_list_pool) error (%d)\n", errno);
579 return -1;
580 }
581
582 p_section_list_pool = NULL;
583
584 return 0;
585 }
586
587 inline static void sid_to_str(int32_t sid, char *p_sid_str)
588 {
589 uint32_t u_sid;
590 int i;
591
592 u_sid = (uint32_t)sid;
593 for (i = 0; i < SID_STR_LEN - 1; i++)
594 {
595 p_sid_str[i] = (char)(u_sid % 255 + 1);
596 u_sid /= 255;
597 }
598 p_sid_str[i] = '\0';
599 }
600
601 SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_list)
602 {
603 SECTION_LIST *p_section;
604 char sid_str[SID_STR_LEN];
605
606 if (p_section_list_pool == NULL)
607 {
608 log_error("session_list_pool not initialized\n");
609 return NULL;
610 }
611
612 if (p_section_list_pool->section_count >= BBS_max_section)
613 {
614 log_error("section_count reach limit %d >= %d\n", p_section_list_pool->section_count, BBS_max_section);
615 return NULL;
616 }
617
618 sid_to_str(sid, sid_str);
619
620 p_section = p_section_list_pool->sections + p_section_list_pool->section_count;
621
622 p_section->sid = sid;
623 p_section->ex_menu_tm = 0;
624
625 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
626 p_section->sname[sizeof(p_section->sname) - 1] = '\0';
627
628 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
629 p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
630
631 strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
632 p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
633
634 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, p_section_list_pool->section_count) != 1)
635 {
636 log_error("trie_dict_set(section, %s, %d) error\n", sname, p_section_list_pool->section_count);
637 return NULL;
638 }
639
640 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, p_section_list_pool->section_count) != 1)
641 {
642 log_error("trie_dict_set(section, %d, %d) error\n", sid, p_section_list_pool->section_count);
643 return NULL;
644 }
645
646 section_list_reset_articles(p_section);
647
648 p_section_list_pool->section_count++;
649
650 return p_section;
651 }
652
653 int section_list_update(SECTION_LIST *p_section, const char *sname, const char *stitle, const char *master_list)
654 {
655 int64_t index;
656
657 if (p_section == NULL || sname == NULL || stitle == NULL || master_list == NULL)
658 {
659 log_error("NULL pointer error\n");
660 return -1;
661 }
662
663 index = get_section_index(p_section);
664
665 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
666 p_section->sname[sizeof(p_section->sname) - 1] = '\0';
667
668 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
669 p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
670
671 strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
672 p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
673
674 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, index) < 0)
675 {
676 log_error("trie_dict_set(section, %s, %d) error\n", sname, index);
677 return -2;
678 }
679
680 return 0;
681 }
682
683 void section_list_reset_articles(SECTION_LIST *p_section)
684 {
685 p_section->article_count = 0;
686 p_section->topic_count = 0;
687 p_section->visible_article_count = 0;
688 p_section->visible_topic_count = 0;
689 p_section->p_article_head = NULL;
690 p_section->p_article_tail = NULL;
691
692 p_section->page_count = 0;
693 p_section->last_page_visible_article_count = 0;
694
695 p_section->ontop_article_count = 0;
696 }
697
698 SECTION_LIST *section_list_find_by_name(const char *sname)
699 {
700 int64_t index;
701 int ret;
702
703 if (p_section_list_pool == NULL)
704 {
705 log_error("section_list not initialized\n");
706 return NULL;
707 }
708
709 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_name, sname, &index);
710 if (ret < 0)
711 {
712 log_error("trie_dict_get(section, %s) error\n", sname);
713 return NULL;
714 }
715 else if (ret == 0)
716 {
717 return NULL;
718 }
719
720 return (p_section_list_pool->sections + index);
721 }
722
723 SECTION_LIST *section_list_find_by_sid(int32_t sid)
724 {
725 int64_t index;
726 int ret;
727 char sid_str[SID_STR_LEN];
728
729 if (p_section_list_pool == NULL)
730 {
731 log_error("section_list not initialized\n");
732 return NULL;
733 }
734
735 sid_to_str(sid, sid_str);
736
737 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, &index);
738 if (ret < 0)
739 {
740 log_error("trie_dict_get(section, %d) error\n", sid);
741 return NULL;
742 }
743 else if (ret == 0)
744 {
745 return NULL;
746 }
747
748 return (p_section_list_pool->sections + index);
749 }
750
751 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
752 {
753 ARTICLE_BLOCK *p_block;
754 int32_t last_aid = 0;
755 ARTICLE *p_article;
756 ARTICLE *p_topic_head;
757 ARTICLE *p_topic_tail;
758
759 if (p_section == NULL || p_article_src == NULL)
760 {
761 log_error("NULL pointer error\n");
762 return -1;
763 }
764
765 if (p_article_block_pool == NULL)
766 {
767 log_error("article_block_pool not initialized\n");
768 return -1;
769 }
770
771 if (p_section->sid != p_article_src->sid)
772 {
773 log_error("section_list_append_article() error: section sid %d != article sid %d\n", p_section->sid, p_article_src->sid);
774 return -2;
775 }
776
777 if (p_section->article_count >= BBS_article_limit_per_section)
778 {
779 log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
780 return -2;
781 }
782
783 if (p_article_block_pool->block_count == 0 ||
784 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)
785 {
786 if ((p_block = pop_free_article_block()) == NULL)
787 {
788 log_error("pop_free_article_block() error\n");
789 return -2;
790 }
791
792 if (p_article_block_pool->block_count > 0)
793 {
794 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;
795 }
796
797 p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
798 p_article_block_pool->block_count++;
799 }
800 else
801 {
802 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
803 last_aid = p_block->articles[p_block->article_count - 1].aid;
804 }
805
806 // AID of articles should be strictly ascending
807 if (p_article_src->aid <= last_aid)
808 {
809 log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
810 return -3;
811 }
812
813 p_article = (p_block->articles + p_block->article_count);
814 p_block->article_count++;
815 p_section->article_count++;
816
817 // Copy article data
818 *p_article = *p_article_src;
819
820 if (p_article->visible)
821 {
822 p_section->visible_article_count++;
823 }
824
825 // Link appended article as tail node of topic bi-directional list
826 if (p_article->tid != 0)
827 {
828 p_topic_head = article_block_find_by_aid(p_article->tid);
829 if (p_topic_head == NULL)
830 {
831 log_error("search head of topic (aid=%d) error\n", p_article->tid);
832 return -4;
833 }
834
835 p_topic_tail = p_topic_head->p_topic_prior;
836 if (p_topic_tail == NULL)
837 {
838 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
839 return -4;
840 }
841 }
842 else
843 {
844 p_section->topic_count++;
845
846 if (p_article->visible)
847 {
848 p_section->visible_topic_count++;
849 }
850
851 p_topic_head = p_article;
852 p_topic_tail = p_article;
853 }
854
855 p_article->p_topic_prior = p_topic_tail;
856 p_article->p_topic_next = p_topic_head;
857 p_topic_head->p_topic_prior = p_article;
858 p_topic_tail->p_topic_next = p_article;
859
860 // Link appended article as tail node of article bi-directional list
861 if (p_section->p_article_head == NULL)
862 {
863 p_section->p_article_head = p_article;
864 p_section->p_article_tail = p_article;
865 }
866 p_article->p_prior = p_section->p_article_tail;
867 p_article->p_next = p_section->p_article_head;
868 p_section->p_article_head->p_prior = p_article;
869 p_section->p_article_tail->p_next = p_article;
870 p_section->p_article_tail = p_article;
871
872 // Update page
873 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
874 p_section->article_count == 1)
875 {
876 p_section->p_page_first_article[p_section->page_count] = p_article;
877 p_section->page_count++;
878 p_section->last_page_visible_article_count = 0;
879 }
880
881 if (p_article->visible)
882 {
883 p_section->last_page_visible_article_count++;
884 }
885
886 if (p_article->ontop && section_list_update_article_ontop(p_section, p_article) < 0)
887 {
888 log_error("section_list_update_article_ontop(sid=%d, aid=%d) error\n",
889 p_section->sid, p_article->aid);
890 return -5;
891 }
892
893 return 0;
894 }
895
896 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
897 {
898 ARTICLE *p_article;
899 ARTICLE *p_reply;
900 int affected_count = 0;
901
902 if (p_section == NULL)
903 {
904 log_error("NULL pointer error\n");
905 return -1;
906 }
907
908 p_article = article_block_find_by_aid(aid);
909 if (p_article == NULL)
910 {
911 return -1; // Not found
912 }
913
914 if (p_section->sid != p_article->sid)
915 {
916 log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
917 return -2;
918 }
919
920 if (p_article->visible == visible)
921 {
922 return 0; // Already set
923 }
924
925 if (visible == 0) // 1 -> 0
926 {
927 p_section->visible_article_count--;
928
929 if (user_article_cnt_inc(p_article->uid, -1) < 0)
930 {
931 log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_article->uid);
932 }
933
934 if (p_article->tid == 0)
935 {
936 p_section->visible_topic_count--;
937
938 // Set related visible replies to invisible
939 for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
940 {
941 if (p_reply->tid != aid)
942 {
943 log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
944 continue;
945 }
946
947 if (p_reply->visible == 1)
948 {
949 p_reply->visible = 0;
950 p_section->visible_article_count--;
951 affected_count++;
952
953 if (user_article_cnt_inc(p_reply->uid, -1) < 0)
954 {
955 log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_reply->uid);
956 }
957 }
958 }
959 }
960 }
961 else // 0 -> 1
962 {
963 p_section->visible_article_count++;
964
965 if (p_article->tid == 0)
966 {
967 p_section->visible_topic_count++;
968 }
969
970 if (user_article_cnt_inc(p_article->uid, 1) < 0)
971 {
972 log_error("user_article_cnt_inc(uid=%d, 1) error\n", p_article->uid);
973 }
974 }
975
976 p_article->visible = visible;
977 affected_count++;
978
979 return affected_count;
980 }
981
982 int section_list_update_article_ontop(SECTION_LIST *p_section, ARTICLE *p_article)
983 {
984 int i;
985
986 if (p_section == NULL || p_article == NULL)
987 {
988 log_error("NULL pointer error\n");
989 return -1;
990 }
991
992 if (p_section->sid != p_article->sid)
993 {
994 log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
995 return -2;
996 }
997
998 if (p_article->ontop)
999 {
1000 for (i = 0; i < p_section->ontop_article_count; i++)
1001 {
1002 if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1003 {
1004 log_error("Inconsistent state found: article %d already ontop in section %d\n", p_article->aid, p_section->sid);
1005 return 0;
1006 }
1007 else if (p_section->p_ontop_articles[i]->aid > p_article->aid)
1008 {
1009 break;
1010 }
1011 }
1012
1013 // Remove the oldest one if the array of ontop articles is full
1014 if (p_section->ontop_article_count >= BBS_ontop_article_limit_per_section)
1015 {
1016 if (i == 0) // p_article is the oldest one
1017 {
1018 return 0;
1019 }
1020 memmove((void *)(p_section->p_ontop_articles),
1021 (void *)(p_section->p_ontop_articles + 1),
1022 sizeof(ARTICLE *) * (size_t)(i - 1));
1023 p_section->ontop_article_count--;
1024 i--;
1025 }
1026 else
1027 {
1028 memmove((void *)(p_section->p_ontop_articles + i + 1),
1029 (void *)(p_section->p_ontop_articles + i),
1030 sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i));
1031 }
1032
1033 p_section->p_ontop_articles[i] = p_article;
1034 p_section->ontop_article_count++;
1035
1036 // TODO: debug
1037 }
1038 else // ontop == 0
1039 {
1040 for (i = 0; i < p_section->ontop_article_count; i++)
1041 {
1042 if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1043 {
1044 break;
1045 }
1046 }
1047 if (i == p_section->ontop_article_count) // not found
1048 {
1049 log_error("Inconsistent state found: article %d not ontop in section %d\n", p_article->aid, p_section->sid);
1050 return 0;
1051 }
1052
1053 memmove((void *)(p_section->p_ontop_articles + i),
1054 (void *)(p_section->p_ontop_articles + i + 1),
1055 sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i - 1));
1056 p_section->ontop_article_count--;
1057 }
1058
1059 return 0;
1060 }
1061
1062 int section_list_page_count_with_ontop(SECTION_LIST *p_section)
1063 {
1064 int page_count;
1065
1066 if (p_section == NULL)
1067 {
1068 log_error("NULL pointer error\n");
1069 return -1;
1070 }
1071
1072 page_count = p_section->page_count - (p_section->last_page_visible_article_count > 0 ? 1 : 0) +
1073 (p_section->last_page_visible_article_count + p_section->ontop_article_count) / BBS_article_limit_per_page +
1074 ((p_section->last_page_visible_article_count + p_section->ontop_article_count) % BBS_article_limit_per_page == 0 ? 0 : 1);
1075
1076 return page_count;
1077 }
1078
1079 int section_list_page_article_count_with_ontop(SECTION_LIST *p_section, int32_t page_id)
1080 {
1081 if (p_section == NULL)
1082 {
1083 log_error("NULL pointer error\n");
1084 return -1;
1085 }
1086
1087 if (page_id < p_section->page_count - 1)
1088 {
1089 return BBS_article_limit_per_page;
1090 }
1091 else // if (page_id >= p_section->page_count - 1)
1092 {
1093 return MAX(0, (p_section->last_page_visible_article_count + p_section->ontop_article_count -
1094 BBS_article_limit_per_page * (page_id - p_section->page_count + 1)));
1095 }
1096 }
1097
1098 ARTICLE *section_list_find_article_with_offset(SECTION_LIST *p_section, int32_t aid, int32_t *p_page, int32_t *p_offset, ARTICLE **pp_next)
1099 {
1100 ARTICLE *p_article;
1101 int left;
1102 int right;
1103 int mid;
1104
1105 *p_page = -1;
1106 *p_offset = -1;
1107 *pp_next = NULL;
1108
1109 if (p_section == NULL)
1110 {
1111 log_error("NULL pointer error\n");
1112 return NULL;
1113 }
1114
1115 if (p_section->article_count == 0) // empty
1116 {
1117 *p_page = 0;
1118 *p_offset = 0;
1119 return NULL;
1120 }
1121
1122 left = 0;
1123 right = p_section->page_count;
1124
1125 // aid in the range [ head aid of pages[left], tail aid of pages[right - 1] ]
1126 while (left < right - 1)
1127 {
1128 // get page id no less than mid value of left page id and right page id
1129 mid = (left + right) / 2 + (right - left) % 2;
1130
1131 if (mid >= p_section->page_count)
1132 {
1133 log_error("page id (mid = %d) is out of boundary\n", mid);
1134 return NULL;
1135 }
1136
1137 if (aid < p_section->p_page_first_article[mid]->aid)
1138 {
1139 right = mid;
1140 }
1141 else
1142 {
1143 left = mid;
1144 }
1145 }
1146
1147 *p_page = left;
1148
1149 p_article = p_section->p_page_first_article[*p_page];
1150
1151 // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
1152 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
1153
1154 // left will be the offset of article found or offset to insert
1155 left = 0;
1156
1157 while (aid > p_article->aid)
1158 {
1159 p_article = p_article->p_next;
1160 left++;
1161
1162 if (aid == p_article->aid)
1163 {
1164 *pp_next = p_article->p_next;
1165 break;
1166 }
1167
1168 // over last article in the page
1169 if (p_article == p_section->p_article_head || p_article->aid >= right)
1170 {
1171 *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
1172 *p_offset = left;
1173 return NULL; // not found
1174 }
1175 }
1176
1177 if (aid < p_article->aid)
1178 {
1179 *pp_next = p_article;
1180 p_article = NULL; // not found
1181 }
1182 else // aid == p_article->aid
1183 {
1184 *pp_next = p_article->p_next;
1185 }
1186
1187 *p_offset = left;
1188
1189 return p_article;
1190 }
1191
1192 int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
1193 {
1194 ARTICLE *p_article;
1195 ARTICLE *p_next;
1196 int32_t page;
1197 int32_t offset;
1198 int visible_article_count;
1199 int page_head_set;
1200
1201 if (p_section == NULL)
1202 {
1203 log_error("NULL pointer error\n");
1204 return -1;
1205 }
1206
1207 if (p_section->article_count == 0) // empty
1208 {
1209 p_section->page_count = 0;
1210 p_section->last_page_visible_article_count = 0;
1211
1212 return 0;
1213 }
1214
1215 if (start_aid > 0)
1216 {
1217 p_article = article_block_find_by_aid(start_aid);
1218 if (p_article == NULL)
1219 {
1220 return -1; // Not found
1221 }
1222
1223 if (p_section->sid != p_article->sid)
1224 {
1225 log_error("section_list_calculate_page() error: section sid %d != start article sid %d\n", p_section->sid, p_article->sid);
1226 return -2;
1227 }
1228
1229 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
1230 if (p_article == NULL)
1231 {
1232 if (page < 0)
1233 {
1234 return -1;
1235 }
1236 log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
1237 start_aid, p_section->sid);
1238 return -2;
1239 }
1240
1241 if (offset > 0)
1242 {
1243 p_article = p_section->p_page_first_article[page];
1244 }
1245 }
1246 else
1247 {
1248 p_article = p_section->p_article_head;
1249 page = 0;
1250 offset = 0;
1251 }
1252
1253 visible_article_count = 0;
1254 page_head_set = 0;
1255
1256 do
1257 {
1258 if (!page_head_set && visible_article_count == 0)
1259 {
1260 p_section->p_page_first_article[page] = p_article;
1261 page_head_set = 1;
1262 }
1263
1264 if (p_article->visible)
1265 {
1266 visible_article_count++;
1267 }
1268
1269 p_article = p_article->p_next;
1270
1271 // skip remaining invisible articles
1272 while (p_article->visible == 0 && p_article != p_section->p_article_head)
1273 {
1274 p_article = p_article->p_next;
1275 }
1276
1277 if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
1278 {
1279 page++;
1280 visible_article_count = 0;
1281 page_head_set = 0;
1282
1283 if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
1284 {
1285 log_error("Count of page exceed limit in section %d\n", p_section->sid);
1286 break;
1287 }
1288 }
1289 } while (p_article != p_section->p_article_head);
1290
1291 p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
1292 p_section->last_page_visible_article_count = visible_article_count;
1293
1294 return 0;
1295 }
1296
1297 int32_t article_block_last_aid(void)
1298 {
1299 ARTICLE_BLOCK *p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
1300 int32_t last_aid = p_block->articles[p_block->article_count - 1].aid;
1301
1302 return last_aid;
1303 }
1304
1305 int article_block_article_count(void)
1306 {
1307 int ret;
1308
1309 if (p_article_block_pool == NULL || p_article_block_pool->block_count <= 0)
1310 {
1311 return -1;
1312 }
1313
1314 ret = (p_article_block_pool->block_count - 1) * ARTICLE_PER_BLOCK +
1315 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count;
1316
1317 return ret;
1318 }
1319
1320 int article_count_of_topic(int32_t aid)
1321 {
1322 ARTICLE *p_article;
1323 int article_count;
1324
1325 p_article = article_block_find_by_aid(aid);
1326 if (p_article == NULL)
1327 {
1328 return 0; // Not found
1329 }
1330
1331 article_count = 0;
1332
1333 do
1334 {
1335 if (p_article->tid != 0 && p_article->tid != aid)
1336 {
1337 log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1338 break;
1339 }
1340
1341 article_count++;
1342 p_article = p_article->p_topic_next;
1343 } while (p_article->aid != aid);
1344
1345 return article_count;
1346 }
1347
1348 int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
1349 {
1350 ARTICLE *p_article;
1351 ARTICLE *p_next;
1352 int32_t page;
1353 int32_t offset;
1354 int32_t move_article_count;
1355 int32_t dest_article_count_old;
1356 int32_t last_unaffected_aid_src;
1357 int32_t first_inserted_aid_dest;
1358 int move_counter;
1359
1360 if (p_section_src == NULL || p_section_dest == NULL)
1361 {
1362 log_error("NULL pointer error\n");
1363 return -1;
1364 }
1365
1366 if (p_section_src->sid == p_section_dest->sid)
1367 {
1368 log_error("section_list_move_topic() src and dest section are the same\n");
1369 return -1;
1370 }
1371
1372 if ((p_article = article_block_find_by_aid(aid)) == NULL)
1373 {
1374 log_error("article_block_find_by_aid(aid = %d) error: article not found\n", aid);
1375 return -2;
1376 }
1377
1378 if (p_section_src->sid != p_article->sid)
1379 {
1380 log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1381 p_section_src->sid, p_article->aid, p_article->sid);
1382 return -2;
1383 }
1384
1385 if (p_article->tid != 0)
1386 {
1387 log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1388 return -2;
1389 }
1390
1391 last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1392
1393 move_article_count = article_count_of_topic(aid);
1394 if (move_article_count <= 0)
1395 {
1396 log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1397 return -2;
1398 }
1399
1400 if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1401 {
1402 log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1403 p_section_dest->article_count + move_article_count, p_section_dest->sid);
1404 return -3;
1405 }
1406
1407 dest_article_count_old = p_section_dest->article_count;
1408 move_counter = 0;
1409 first_inserted_aid_dest = p_article->aid;
1410
1411 do
1412 {
1413 if (p_section_src->sid != p_article->sid)
1414 {
1415 log_error("section_list_move_topic() warning: src section sid %d != article %d sid %d\n",
1416 p_section_src->sid, p_article->aid, p_article->sid);
1417 p_article = p_article->p_topic_next;
1418 continue;
1419 }
1420
1421 // Remove from bi-directional article list of src section
1422 if (p_section_src->p_article_head == p_article)
1423 {
1424 p_section_src->p_article_head = p_article->p_next;
1425 }
1426 if (p_section_src->p_article_tail == p_article)
1427 {
1428 p_section_src->p_article_tail = p_article->p_prior;
1429 }
1430 if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1431 {
1432 p_section_src->p_article_head = NULL;
1433 p_section_src->p_article_tail = NULL;
1434 }
1435
1436 p_article->p_prior->p_next = p_article->p_next;
1437 p_article->p_next->p_prior = p_article->p_prior;
1438
1439 // Update sid of article
1440 p_article->sid = p_section_dest->sid;
1441
1442 if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1443 {
1444 log_error("section_list_move_topic() warning: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1445 p_article = p_article->p_topic_next;
1446 continue;
1447 }
1448
1449 // Insert into bi-directional article list of dest section
1450 if (p_next == NULL) // empty section
1451 {
1452 p_section_dest->p_article_head = p_article;
1453 p_section_dest->p_article_tail = p_article;
1454 p_article->p_prior = p_article;
1455 p_article->p_next = p_article;
1456 }
1457 else
1458 {
1459 if (p_section_dest->p_article_head == p_next)
1460 {
1461 if (p_article->aid < p_next->aid)
1462 {
1463 p_section_dest->p_article_head = p_article;
1464 }
1465 else // p_article->aid > p_next->aid
1466 {
1467 p_section_dest->p_article_tail = p_article;
1468 }
1469 }
1470
1471 p_article->p_prior = p_next->p_prior;
1472 p_article->p_next = p_next;
1473 p_next->p_prior->p_next = p_article;
1474 p_next->p_prior = p_article;
1475 }
1476
1477 // Update article / topic counter of src / desc section
1478 p_section_src->article_count--;
1479 p_section_dest->article_count++;
1480 if (p_article->tid == 0)
1481 {
1482 p_section_src->topic_count--;
1483 p_section_dest->topic_count++;
1484 }
1485
1486 // Update visible article / topic counter of src / desc section
1487 if (p_article->visible)
1488 {
1489 p_section_src->visible_article_count--;
1490 p_section_dest->visible_article_count++;
1491 if (p_article->tid == 0)
1492 {
1493 p_section_src->visible_topic_count--;
1494 p_section_dest->visible_topic_count++;
1495 }
1496 }
1497
1498 // Update page for empty dest section
1499 if (p_section_dest->article_count == 1)
1500 {
1501 p_section_dest->p_page_first_article[0] = p_article;
1502 p_section_dest->page_count = 1;
1503 p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1504 }
1505
1506 p_article = p_article->p_topic_next;
1507
1508 move_counter++;
1509 if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1510 {
1511 // Re-calculate pages of desc section
1512 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1513 {
1514 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1515 p_section_dest->sid, first_inserted_aid_dest);
1516 }
1517
1518 first_inserted_aid_dest = p_article->aid;
1519 }
1520 } while (p_article->aid != aid);
1521
1522 if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1523 {
1524 log_error("section_list_move_topic() warning: count of moved articles %d != %d\n",
1525 p_section_dest->article_count - dest_article_count_old, move_article_count);
1526 }
1527
1528 // Re-calculate pages of src section
1529 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1530 {
1531 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1532 p_section_src->sid, last_unaffected_aid_src, aid);
1533 }
1534
1535 if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1536 {
1537 // Re-calculate pages of desc section
1538 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1539 {
1540 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1541 p_section_dest->sid, first_inserted_aid_dest);
1542 }
1543 }
1544
1545 return move_article_count;
1546 }
1547
1548 int get_section_index(SECTION_LIST *p_section)
1549 {
1550 int index;
1551
1552 if (p_section_list_pool == NULL)
1553 {
1554 log_error("get_section_index() error: uninitialized\n");
1555 return -1;
1556 }
1557
1558 if (p_section == NULL)
1559 {
1560 index = BBS_max_section;
1561 }
1562 else
1563 {
1564 index = (int)(p_section - p_section_list_pool->sections);
1565 if (index < 0 || index >= BBS_max_section)
1566 {
1567 log_error("get_section_index(%d) error: index out of range\n", index);
1568 return -2;
1569 }
1570 }
1571
1572 return index;
1573 }
1574
1575 int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1576 {
1577 int index;
1578 struct sembuf sops[4];
1579 struct timespec timeout;
1580 int ret;
1581
1582 index = get_section_index(p_section);
1583 if (index < 0)
1584 {
1585 return -2;
1586 }
1587
1588 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1589 sops[0].sem_op = 0; // wait until unlocked
1590 sops[0].sem_flg = 0;
1591
1592 sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1593 sops[1].sem_op = 1; // lock
1594 sops[1].sem_flg = SEM_UNDO; // undo on terminate
1595
1596 // Read lock on any specific section will also acquire single read lock on "all section"
1597 // so that write lock on all section only need to acquire single write on on "all section"
1598 // rather than to acquire multiple write locks on all the available sections.
1599 if (index != BBS_max_section)
1600 {
1601 sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1602 sops[2].sem_op = 0; // wait until unlocked
1603 sops[2].sem_flg = 0;
1604
1605 sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1606 sops[3].sem_op = 1; // lock
1607 sops[3].sem_flg = SEM_UNDO; // undo on terminate
1608 }
1609
1610 timeout.tv_sec = wait_sec;
1611 timeout.tv_nsec = 0;
1612
1613 ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1614 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1615 {
1616 log_error("semtimedop(index = %d, lock read) error %d\n", index, errno);
1617 }
1618
1619 return ret;
1620 }
1621
1622 int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1623 {
1624 int index;
1625 struct sembuf sops[3];
1626 struct timespec timeout;
1627 int ret;
1628
1629 index = get_section_index(p_section);
1630 if (index < 0)
1631 {
1632 return -2;
1633 }
1634
1635 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1636 sops[0].sem_op = 0; // wait until unlocked
1637 sops[0].sem_flg = 0;
1638
1639 sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1640 sops[1].sem_op = 1; // lock
1641 sops[1].sem_flg = SEM_UNDO; // undo on terminate
1642
1643 sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1644 sops[2].sem_op = 0; // wait until unlocked
1645 sops[2].sem_flg = 0;
1646
1647 timeout.tv_sec = wait_sec;
1648 timeout.tv_nsec = 0;
1649
1650 ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1651 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1652 {
1653 log_error("semtimedop(index = %d, lock write) error %d\n", index, errno);
1654 }
1655
1656 return ret;
1657 }
1658
1659 int section_list_rd_unlock(SECTION_LIST *p_section)
1660 {
1661 int index;
1662 struct sembuf sops[2];
1663 int ret;
1664
1665 index = get_section_index(p_section);
1666 if (index < 0)
1667 {
1668 return -2;
1669 }
1670
1671 sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1672 sops[0].sem_op = -1; // unlock
1673 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1674
1675 if (index != BBS_max_section)
1676 {
1677 sops[1].sem_num = BBS_max_section * 2; // r_sem of all section
1678 sops[1].sem_op = -1; // unlock
1679 sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1680 }
1681
1682 ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1683 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1684 {
1685 log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1686 }
1687
1688 return ret;
1689 }
1690
1691 int section_list_rw_unlock(SECTION_LIST *p_section)
1692 {
1693 int index;
1694 struct sembuf sops[1];
1695 int ret;
1696
1697 index = get_section_index(p_section);
1698 if (index < 0)
1699 {
1700 return -2;
1701 }
1702
1703 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1704 sops[0].sem_op = -1; // unlock
1705 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1706
1707 ret = semop(p_section_list_pool->semid, sops, 1);
1708 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1709 {
1710 log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1711 }
1712
1713 return ret;
1714 }
1715
1716 int section_list_rd_lock(SECTION_LIST *p_section)
1717 {
1718 int timer = 0;
1719 int sid = (p_section == NULL ? 0 : p_section->sid);
1720 int ret = -1;
1721
1722 while (!SYS_server_exit)
1723 {
1724 ret = section_list_try_rd_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1725 if (ret == 0) // success
1726 {
1727 break;
1728 }
1729 else if (errno == EAGAIN || errno == EINTR) // retry
1730 {
1731 timer++;
1732 if (timer % SECTION_TRY_LOCK_TIMES == 0)
1733 {
1734 log_error("section_list_try_rd_lock() tried %d times on section %d\n", timer, sid);
1735 }
1736 }
1737 else // failed
1738 {
1739 log_error("section_list_try_rd_lock() failed on section %d\n", sid);
1740 break;
1741 }
1742 }
1743
1744 return ret;
1745 }
1746
1747 int section_list_rw_lock(SECTION_LIST *p_section)
1748 {
1749 int timer = 0;
1750 int sid = (p_section == NULL ? 0 : p_section->sid);
1751 int ret = -1;
1752
1753 while (!SYS_server_exit)
1754 {
1755 ret = section_list_try_rw_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1756 if (ret == 0) // success
1757 {
1758 break;
1759 }
1760 else if (errno == EAGAIN || errno == EINTR) // retry
1761 {
1762 timer++;
1763 if (timer % SECTION_TRY_LOCK_TIMES == 0)
1764 {
1765 log_error("section_list_try_rw_lock() tried %d times on section %d\n", timer, sid);
1766 }
1767 }
1768 else // failed
1769 {
1770 log_error("section_list_try_rw_lock() failed on section %d\n", sid);
1771 break;
1772 }
1773 }
1774
1775 return ret;
1776 }

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