/[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.55 - (show annotations)
Wed Nov 5 04:19:21 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.54: +10 -7 lines
Content type: text/x-csrc
Use enum / const int instead of macro define constant integers
Use const char * instead of macro define for constant strings

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

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