/[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.49 - (show annotations)
Mon Nov 3 08:48:56 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.48: +4 -2 lines
Content type: text/x-csrc
Fix bug

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 - 1;
347
348 // aid in the range [ head aid of blocks[left], tail aid of blocks[right] ]
349 while (left < right)
350 {
351 // get block offset no less than mid value of left and right block offsets
352 mid = (left + right) / 2 + (left + right) % 2;
353
354 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
355 {
356 right = mid - 1;
357 }
358 else // if (aid >= p_article_block_pool->p_block[mid]->articles[0].aid)
359 {
360 left = mid;
361 }
362 }
363
364 p_block = p_article_block_pool->p_block[left];
365
366 left = 0;
367 right = p_block->article_count - 1;
368
369 // aid in the range [ aid of articles[left], aid of articles[right] ]
370 while (left < right)
371 {
372 mid = (left + right) / 2;
373
374 if (aid <= p_block->articles[mid].aid)
375 {
376 right = mid;
377 }
378 else
379 {
380 left = mid + 1;
381 }
382 }
383
384 if (aid != p_block->articles[left].aid) // not found
385 {
386 return NULL;
387 }
388
389 return (p_block->articles + left); // found
390 }
391
392 ARTICLE *article_block_find_by_index(int index)
393 {
394 ARTICLE_BLOCK *p_block;
395
396 if (p_article_block_pool == NULL)
397 {
398 log_error("article_block_pool not initialized\n");
399 return NULL;
400 }
401
402 if (index < 0 || index / ARTICLE_PER_BLOCK >= p_article_block_pool->block_count)
403 {
404 log_error("article_block_find_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
405 return NULL;
406 }
407
408 p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];
409
410 if (index % ARTICLE_PER_BLOCK >= p_block->article_count)
411 {
412 log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
413 return NULL;
414 }
415
416 return (p_block->articles + (index % ARTICLE_PER_BLOCK));
417 }
418
419 extern int section_list_init(const char *filename)
420 {
421 int semid;
422 int shmid;
423 int proj_id;
424 key_t key;
425 size_t size;
426 void *p_shm;
427 union semun arg;
428 int i;
429
430 if (p_section_list_pool != NULL)
431 {
432 section_list_cleanup();
433 }
434
435 proj_id = (int)(time(NULL) % getpid());
436 key = ftok(filename, proj_id);
437 if (key == -1)
438 {
439 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
440 return -3;
441 }
442
443 // Allocate shared memory
444 size = sizeof(SECTION_LIST_POOL);
445 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
446 if (shmid == -1)
447 {
448 log_error("shmget(section_list_pool_shm, size = %d) error (%d)\n", size, errno);
449 return -3;
450 }
451 p_shm = shmat(shmid, NULL, 0);
452 if (p_shm == (void *)-1)
453 {
454 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
455 return -3;
456 }
457
458 p_section_list_pool = p_shm;
459 p_section_list_pool->shmid = shmid;
460 p_section_list_pool->section_count = 0;
461
462 // Allocate semaphore as section locks
463 size = 2 * (BBS_max_section + 1); // r_sem and w_sem per section, the last pair for all sections
464 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
465 if (semid == -1)
466 {
467 log_error("semget(section_list_pool_sem, size = %d) error (%d)\n", size, errno);
468 return -3;
469 }
470
471 // Initialize sem value to 0
472 arg.val = 0;
473 for (i = 0; i < size; i++)
474 {
475 if (semctl(semid, i, SETVAL, arg) == -1)
476 {
477 log_error("semctl(section_list_pool_sem, SETVAL) error (%d)\n", errno);
478 return -3;
479 }
480 }
481
482 p_section_list_pool->semid = semid;
483
484 p_section_list_pool->p_trie_dict_section_by_name = trie_dict_create();
485 if (p_section_list_pool->p_trie_dict_section_by_name == NULL)
486 {
487 log_error("trie_dict_create() OOM\n", BBS_max_section);
488 return -2;
489 }
490
491 p_section_list_pool->p_trie_dict_section_by_sid = trie_dict_create();
492 if (p_section_list_pool->p_trie_dict_section_by_sid == NULL)
493 {
494 log_error("trie_dict_create() OOM\n", BBS_max_section);
495 return -2;
496 }
497
498 return 0;
499 }
500
501 void section_list_cleanup(void)
502 {
503 int shmid;
504
505 if (p_section_list_pool == NULL)
506 {
507 return;
508 }
509
510 if (p_section_list_pool->p_trie_dict_section_by_name != NULL)
511 {
512 trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_name);
513 p_section_list_pool->p_trie_dict_section_by_name = NULL;
514 }
515
516 if (p_section_list_pool->p_trie_dict_section_by_sid != NULL)
517 {
518 trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_sid);
519 p_section_list_pool->p_trie_dict_section_by_sid = NULL;
520 }
521
522 shmid = p_section_list_pool->shmid;
523
524 if (semctl(p_section_list_pool->semid, 0, IPC_RMID) == -1)
525 {
526 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_section_list_pool->semid, errno);
527 }
528
529 if (shmdt(p_section_list_pool) == -1)
530 {
531 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
532 }
533
534 if (shmctl(shmid, IPC_RMID, NULL) == -1)
535 {
536 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
537 }
538
539 p_section_list_pool = NULL;
540 }
541
542 int set_section_list_shm_readonly(void)
543 {
544 int shmid;
545 void *p_shm;
546
547 if (p_section_list_pool == NULL)
548 {
549 log_error("p_section_list_pool not initialized\n");
550 return -1;
551 }
552
553 shmid = p_section_list_pool->shmid;
554
555 // Remap shared memory in read-only mode
556 p_shm = shmat(shmid, p_section_list_pool, SHM_RDONLY | SHM_REMAP);
557 if (p_shm == (void *)-1)
558 {
559 log_error("shmat(section_list_pool shmid = %d) error (%d)\n", shmid, errno);
560 return -3;
561 }
562
563 p_section_list_pool = p_shm;
564
565 return 0;
566 }
567
568 int detach_section_list_shm(void)
569 {
570 if (p_section_list_pool != NULL && shmdt(p_section_list_pool) == -1)
571 {
572 log_error("shmdt(section_list_pool) error (%d)\n", errno);
573 return -1;
574 }
575
576 p_section_list_pool = NULL;
577
578 return 0;
579 }
580
581 inline static void sid_to_str(int32_t sid, char *p_sid_str)
582 {
583 uint32_t u_sid;
584 int i;
585
586 u_sid = (uint32_t)sid;
587 for (i = 0; i < SID_STR_LEN - 1; i++)
588 {
589 p_sid_str[i] = (char)(u_sid % 255 + 1);
590 u_sid /= 255;
591 }
592 p_sid_str[i] = '\0';
593 }
594
595 SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_list)
596 {
597 SECTION_LIST *p_section;
598 char sid_str[SID_STR_LEN];
599
600 if (p_section_list_pool == NULL)
601 {
602 log_error("session_list_pool not initialized\n");
603 return NULL;
604 }
605
606 if (p_section_list_pool->section_count >= BBS_max_section)
607 {
608 log_error("section_count reach limit %d >= %d\n", p_section_list_pool->section_count, BBS_max_section);
609 return NULL;
610 }
611
612 sid_to_str(sid, sid_str);
613
614 p_section = p_section_list_pool->sections + p_section_list_pool->section_count;
615
616 p_section->sid = sid;
617 p_section->ex_menu_tm = 0;
618
619 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
620 p_section->sname[sizeof(p_section->sname) - 1] = '\0';
621
622 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
623 p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
624
625 strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
626 p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
627
628 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, p_section_list_pool->section_count) != 1)
629 {
630 log_error("trie_dict_set(section, %s, %d) error\n", sname, p_section_list_pool->section_count);
631 return NULL;
632 }
633
634 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, p_section_list_pool->section_count) != 1)
635 {
636 log_error("trie_dict_set(section, %d, %d) error\n", sid, p_section_list_pool->section_count);
637 return NULL;
638 }
639
640 section_list_reset_articles(p_section);
641
642 p_section_list_pool->section_count++;
643
644 return p_section;
645 }
646
647 int section_list_update(SECTION_LIST *p_section, const char *sname, const char *stitle, const char *master_list)
648 {
649 int64_t index;
650
651 if (p_section == NULL || sname == NULL || stitle == NULL || master_list == NULL)
652 {
653 log_error("NULL pointer error\n");
654 return -1;
655 }
656
657 index = get_section_index(p_section);
658
659 strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
660 p_section->sname[sizeof(p_section->sname) - 1] = '\0';
661
662 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
663 p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
664
665 strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
666 p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
667
668 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, index) < 0)
669 {
670 log_error("trie_dict_set(section, %s, %d) error\n", sname, index);
671 return -2;
672 }
673
674 return 0;
675 }
676
677 void section_list_reset_articles(SECTION_LIST *p_section)
678 {
679 p_section->article_count = 0;
680 p_section->topic_count = 0;
681 p_section->visible_article_count = 0;
682 p_section->visible_topic_count = 0;
683 p_section->p_article_head = NULL;
684 p_section->p_article_tail = NULL;
685
686 p_section->page_count = 0;
687 p_section->last_page_visible_article_count = 0;
688
689 p_section->ontop_article_count = 0;
690 }
691
692 SECTION_LIST *section_list_find_by_name(const char *sname)
693 {
694 int64_t index;
695 int ret;
696
697 if (p_section_list_pool == NULL)
698 {
699 log_error("section_list not initialized\n");
700 return NULL;
701 }
702
703 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_name, sname, &index);
704 if (ret < 0)
705 {
706 log_error("trie_dict_get(section, %s) error\n", sname);
707 return NULL;
708 }
709 else if (ret == 0)
710 {
711 return NULL;
712 }
713
714 return (p_section_list_pool->sections + index);
715 }
716
717 SECTION_LIST *section_list_find_by_sid(int32_t sid)
718 {
719 int64_t index;
720 int ret;
721 char sid_str[SID_STR_LEN];
722
723 if (p_section_list_pool == NULL)
724 {
725 log_error("section_list not initialized\n");
726 return NULL;
727 }
728
729 sid_to_str(sid, sid_str);
730
731 ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, &index);
732 if (ret < 0)
733 {
734 log_error("trie_dict_get(section, %d) error\n", sid);
735 return NULL;
736 }
737 else if (ret == 0)
738 {
739 return NULL;
740 }
741
742 return (p_section_list_pool->sections + index);
743 }
744
745 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
746 {
747 ARTICLE_BLOCK *p_block;
748 int32_t last_aid = 0;
749 ARTICLE *p_article;
750 ARTICLE *p_topic_head;
751 ARTICLE *p_topic_tail;
752
753 if (p_section == NULL || p_article_src == NULL)
754 {
755 log_error("NULL pointer error\n");
756 return -1;
757 }
758
759 if (p_article_block_pool == NULL)
760 {
761 log_error("article_block_pool not initialized\n");
762 return -1;
763 }
764
765 if (p_section->sid != p_article_src->sid)
766 {
767 log_error("section_list_append_article() error: section sid %d != article sid %d\n", p_section->sid, p_article_src->sid);
768 return -2;
769 }
770
771 if (p_section->article_count >= BBS_article_limit_per_section)
772 {
773 log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
774 return -2;
775 }
776
777 if (p_article_block_pool->block_count == 0 ||
778 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)
779 {
780 if ((p_block = pop_free_article_block()) == NULL)
781 {
782 log_error("pop_free_article_block() error\n");
783 return -2;
784 }
785
786 if (p_article_block_pool->block_count > 0)
787 {
788 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;
789 }
790
791 p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
792 p_article_block_pool->block_count++;
793 }
794 else
795 {
796 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
797 last_aid = p_block->articles[p_block->article_count - 1].aid;
798 }
799
800 // AID of articles should be strictly ascending
801 if (p_article_src->aid <= last_aid)
802 {
803 log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
804 return -3;
805 }
806
807 p_article = (p_block->articles + p_block->article_count);
808 p_block->article_count++;
809 p_section->article_count++;
810
811 // Copy article data
812 *p_article = *p_article_src;
813
814 if (p_article->visible)
815 {
816 p_section->visible_article_count++;
817 }
818
819 // Link appended article as tail node of topic bi-directional list
820 if (p_article->tid != 0)
821 {
822 p_topic_head = article_block_find_by_aid(p_article->tid);
823 if (p_topic_head == NULL)
824 {
825 log_error("search head of topic (aid=%d) error\n", p_article->tid);
826 return -4;
827 }
828
829 p_topic_tail = p_topic_head->p_topic_prior;
830 if (p_topic_tail == NULL)
831 {
832 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
833 return -4;
834 }
835 }
836 else
837 {
838 p_section->topic_count++;
839
840 if (p_article->visible)
841 {
842 p_section->visible_topic_count++;
843 }
844
845 p_topic_head = p_article;
846 p_topic_tail = p_article;
847 }
848
849 p_article->p_topic_prior = p_topic_tail;
850 p_article->p_topic_next = p_topic_head;
851 p_topic_head->p_topic_prior = p_article;
852 p_topic_tail->p_topic_next = p_article;
853
854 // Link appended article as tail node of article bi-directional list
855 if (p_section->p_article_head == NULL)
856 {
857 p_section->p_article_head = p_article;
858 p_section->p_article_tail = p_article;
859 }
860 p_article->p_prior = p_section->p_article_tail;
861 p_article->p_next = p_section->p_article_head;
862 p_section->p_article_head->p_prior = p_article;
863 p_section->p_article_tail->p_next = p_article;
864 p_section->p_article_tail = p_article;
865
866 // Update page
867 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
868 p_section->article_count == 1)
869 {
870 p_section->p_page_first_article[p_section->page_count] = p_article;
871 p_section->page_count++;
872 p_section->last_page_visible_article_count = 0;
873 }
874
875 if (p_article->visible)
876 {
877 p_section->last_page_visible_article_count++;
878 }
879
880 if (p_article->ontop && section_list_update_article_ontop(p_section, p_article) < 0)
881 {
882 log_error("section_list_update_article_ontop(sid=%d, aid=%d) error\n",
883 p_section->sid, p_article->aid);
884 return -5;
885 }
886
887 return 0;
888 }
889
890 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
891 {
892 ARTICLE *p_article;
893 ARTICLE *p_reply;
894 int affected_count = 0;
895
896 if (p_section == NULL)
897 {
898 log_error("NULL pointer error\n");
899 return -1;
900 }
901
902 p_article = article_block_find_by_aid(aid);
903 if (p_article == NULL)
904 {
905 return -1; // Not found
906 }
907
908 if (p_section->sid != p_article->sid)
909 {
910 log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
911 return -2;
912 }
913
914 if (p_article->visible == visible)
915 {
916 return 0; // Already set
917 }
918
919 if (visible == 0) // 1 -> 0
920 {
921 p_section->visible_article_count--;
922
923 if (user_article_cnt_inc(p_article->uid, -1) < 0)
924 {
925 log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_article->uid);
926 }
927
928 if (p_article->tid == 0)
929 {
930 p_section->visible_topic_count--;
931
932 // Set related visible replies to invisible
933 for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
934 {
935 if (p_reply->tid != aid)
936 {
937 log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
938 continue;
939 }
940
941 if (p_reply->visible == 1)
942 {
943 p_reply->visible = 0;
944 p_section->visible_article_count--;
945 affected_count++;
946
947 if (user_article_cnt_inc(p_reply->uid, -1) < 0)
948 {
949 log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_reply->uid);
950 }
951 }
952 }
953 }
954 }
955 else // 0 -> 1
956 {
957 p_section->visible_article_count++;
958
959 if (p_article->tid == 0)
960 {
961 p_section->visible_topic_count++;
962 }
963
964 if (user_article_cnt_inc(p_article->uid, 1) < 0)
965 {
966 log_error("user_article_cnt_inc(uid=%d, 1) error\n", p_article->uid);
967 }
968 }
969
970 p_article->visible = visible;
971 affected_count++;
972
973 return affected_count;
974 }
975
976 int section_list_update_article_ontop(SECTION_LIST *p_section, ARTICLE *p_article)
977 {
978 int i;
979
980 if (p_section == NULL || p_article == NULL)
981 {
982 log_error("NULL pointer error\n");
983 return -1;
984 }
985
986 if (p_section->sid != p_article->sid)
987 {
988 log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
989 return -2;
990 }
991
992 if (p_article->ontop)
993 {
994 for (i = 0; i < p_section->ontop_article_count; i++)
995 {
996 if (p_section->p_ontop_articles[i]->aid == p_article->aid)
997 {
998 log_error("Inconsistent state found: article %d already ontop in section %d\n", p_article->aid, p_section->sid);
999 return 0;
1000 }
1001 else if (p_section->p_ontop_articles[i]->aid > p_article->aid)
1002 {
1003 break;
1004 }
1005 }
1006
1007 // Remove the oldest one if the array of ontop articles is full
1008 if (p_section->ontop_article_count >= BBS_ontop_article_limit_per_section)
1009 {
1010 if (i == 0) // p_article is the oldest one
1011 {
1012 return 0;
1013 }
1014 memmove((void *)(p_section->p_ontop_articles),
1015 (void *)(p_section->p_ontop_articles + 1),
1016 sizeof(ARTICLE *) * (size_t)(i - 1));
1017 p_section->ontop_article_count--;
1018 i--;
1019 }
1020 else
1021 {
1022 memmove((void *)(p_section->p_ontop_articles + i + 1),
1023 (void *)(p_section->p_ontop_articles + i),
1024 sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i));
1025 }
1026
1027 p_section->p_ontop_articles[i] = p_article;
1028 p_section->ontop_article_count++;
1029
1030 // TODO: debug
1031 }
1032 else // ontop == 0
1033 {
1034 for (i = 0; i < p_section->ontop_article_count; i++)
1035 {
1036 if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1037 {
1038 break;
1039 }
1040 }
1041 if (i == p_section->ontop_article_count) // not found
1042 {
1043 log_error("Inconsistent state found: article %d not ontop in section %d\n", p_article->aid, p_section->sid);
1044 return 0;
1045 }
1046
1047 memmove((void *)(p_section->p_ontop_articles + i),
1048 (void *)(p_section->p_ontop_articles + i + 1),
1049 sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i - 1));
1050 p_section->ontop_article_count--;
1051 }
1052
1053 return 0;
1054 }
1055
1056 int section_list_page_count_with_ontop(SECTION_LIST *p_section)
1057 {
1058 int page_count;
1059
1060 if (p_section == NULL)
1061 {
1062 log_error("NULL pointer error\n");
1063 return -1;
1064 }
1065
1066 page_count = p_section->page_count - (p_section->last_page_visible_article_count > 0 ? 1 : 0) +
1067 (p_section->last_page_visible_article_count + p_section->ontop_article_count) / BBS_article_limit_per_page +
1068 ((p_section->last_page_visible_article_count + p_section->ontop_article_count) % BBS_article_limit_per_page == 0 ? 0 : 1);
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;
1283
1284 return 0;
1285 }
1286
1287 int32_t article_block_last_aid(void)
1288 {
1289 ARTICLE_BLOCK *p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
1290 int32_t last_aid = p_block->articles[p_block->article_count - 1].aid;
1291
1292 return last_aid;
1293 }
1294
1295 int article_block_article_count(void)
1296 {
1297 int ret;
1298
1299 if (p_article_block_pool == NULL || p_article_block_pool->block_count <= 0)
1300 {
1301 return -1;
1302 }
1303
1304 ret = (p_article_block_pool->block_count - 1) * ARTICLE_PER_BLOCK +
1305 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count;
1306
1307 return ret;
1308 }
1309
1310 int article_count_of_topic(int32_t aid)
1311 {
1312 ARTICLE *p_article;
1313 int article_count;
1314
1315 p_article = article_block_find_by_aid(aid);
1316 if (p_article == NULL)
1317 {
1318 return 0; // Not found
1319 }
1320
1321 article_count = 0;
1322
1323 do
1324 {
1325 if (p_article->tid != 0 && p_article->tid != aid)
1326 {
1327 log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1328 break;
1329 }
1330
1331 article_count++;
1332 p_article = p_article->p_topic_next;
1333 } while (p_article->aid != aid);
1334
1335 return article_count;
1336 }
1337
1338 int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
1339 {
1340 ARTICLE *p_article;
1341 ARTICLE *p_next;
1342 int32_t page;
1343 int32_t offset;
1344 int32_t move_article_count;
1345 int32_t dest_article_count_old;
1346 int32_t last_unaffected_aid_src;
1347 int32_t first_inserted_aid_dest;
1348 int move_counter;
1349
1350 if (p_section_src == NULL || p_section_dest == NULL)
1351 {
1352 log_error("NULL pointer error\n");
1353 return -1;
1354 }
1355
1356 if (p_section_src->sid == p_section_dest->sid)
1357 {
1358 log_error("section_list_move_topic() src and dest section are the same\n");
1359 return -1;
1360 }
1361
1362 if ((p_article = article_block_find_by_aid(aid)) == NULL)
1363 {
1364 log_error("article_block_find_by_aid(aid = %d) error: article not found\n", aid);
1365 return -2;
1366 }
1367
1368 if (p_section_src->sid != p_article->sid)
1369 {
1370 log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1371 p_section_src->sid, p_article->aid, p_article->sid);
1372 return -2;
1373 }
1374
1375 if (p_article->tid != 0)
1376 {
1377 log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1378 return -2;
1379 }
1380
1381 last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1382
1383 move_article_count = article_count_of_topic(aid);
1384 if (move_article_count <= 0)
1385 {
1386 log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1387 return -2;
1388 }
1389
1390 if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1391 {
1392 log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1393 p_section_dest->article_count + move_article_count, p_section_dest->sid);
1394 return -3;
1395 }
1396
1397 dest_article_count_old = p_section_dest->article_count;
1398 move_counter = 0;
1399 first_inserted_aid_dest = p_article->aid;
1400
1401 do
1402 {
1403 if (p_section_src->sid != p_article->sid)
1404 {
1405 log_error("section_list_move_topic() warning: src section sid %d != article %d sid %d\n",
1406 p_section_src->sid, p_article->aid, p_article->sid);
1407 p_article = p_article->p_topic_next;
1408 continue;
1409 }
1410
1411 // Remove from bi-directional article list of src section
1412 if (p_section_src->p_article_head == p_article)
1413 {
1414 p_section_src->p_article_head = p_article->p_next;
1415 }
1416 if (p_section_src->p_article_tail == p_article)
1417 {
1418 p_section_src->p_article_tail = p_article->p_prior;
1419 }
1420 if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1421 {
1422 p_section_src->p_article_head = NULL;
1423 p_section_src->p_article_tail = NULL;
1424 }
1425
1426 p_article->p_prior->p_next = p_article->p_next;
1427 p_article->p_next->p_prior = p_article->p_prior;
1428
1429 // Update sid of article
1430 p_article->sid = p_section_dest->sid;
1431
1432 if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1433 {
1434 log_error("section_list_move_topic() warning: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1435 p_article = p_article->p_topic_next;
1436 continue;
1437 }
1438
1439 // Insert into bi-directional article list of dest section
1440 if (p_next == NULL) // empty section
1441 {
1442 p_section_dest->p_article_head = p_article;
1443 p_section_dest->p_article_tail = p_article;
1444 p_article->p_prior = p_article;
1445 p_article->p_next = p_article;
1446 }
1447 else
1448 {
1449 if (p_section_dest->p_article_head == p_next)
1450 {
1451 if (p_article->aid < p_next->aid)
1452 {
1453 p_section_dest->p_article_head = p_article;
1454 }
1455 else // p_article->aid > p_next->aid
1456 {
1457 p_section_dest->p_article_tail = p_article;
1458 }
1459 }
1460
1461 p_article->p_prior = p_next->p_prior;
1462 p_article->p_next = p_next;
1463 p_next->p_prior->p_next = p_article;
1464 p_next->p_prior = p_article;
1465 }
1466
1467 // Update article / topic counter of src / desc section
1468 p_section_src->article_count--;
1469 p_section_dest->article_count++;
1470 if (p_article->tid == 0)
1471 {
1472 p_section_src->topic_count--;
1473 p_section_dest->topic_count++;
1474 }
1475
1476 // Update visible article / topic counter of src / desc section
1477 if (p_article->visible)
1478 {
1479 p_section_src->visible_article_count--;
1480 p_section_dest->visible_article_count++;
1481 if (p_article->tid == 0)
1482 {
1483 p_section_src->visible_topic_count--;
1484 p_section_dest->visible_topic_count++;
1485 }
1486 }
1487
1488 // Update page for empty dest section
1489 if (p_section_dest->article_count == 1)
1490 {
1491 p_section_dest->p_page_first_article[0] = p_article;
1492 p_section_dest->page_count = 1;
1493 p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1494 }
1495
1496 p_article = p_article->p_topic_next;
1497
1498 move_counter++;
1499 if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1500 {
1501 // Re-calculate pages of desc section
1502 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1503 {
1504 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1505 p_section_dest->sid, first_inserted_aid_dest);
1506 }
1507
1508 first_inserted_aid_dest = p_article->aid;
1509 }
1510 } while (p_article->aid != aid);
1511
1512 if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1513 {
1514 log_error("section_list_move_topic() warning: count of moved articles %d != %d\n",
1515 p_section_dest->article_count - dest_article_count_old, move_article_count);
1516 }
1517
1518 // Re-calculate pages of src section
1519 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1520 {
1521 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1522 p_section_src->sid, last_unaffected_aid_src, aid);
1523 }
1524
1525 if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1526 {
1527 // Re-calculate pages of desc section
1528 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1529 {
1530 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1531 p_section_dest->sid, first_inserted_aid_dest);
1532 }
1533 }
1534
1535 return move_article_count;
1536 }
1537
1538 int get_section_index(SECTION_LIST *p_section)
1539 {
1540 int index;
1541
1542 if (p_section_list_pool == NULL)
1543 {
1544 log_error("get_section_index() error: uninitialized\n");
1545 return -1;
1546 }
1547
1548 if (p_section == NULL)
1549 {
1550 index = BBS_max_section;
1551 }
1552 else
1553 {
1554 index = (int)(p_section - p_section_list_pool->sections);
1555 if (index < 0 || index >= BBS_max_section)
1556 {
1557 log_error("get_section_index(%d) error: index out of range\n", index);
1558 return -2;
1559 }
1560 }
1561
1562 return index;
1563 }
1564
1565 int get_section_info(SECTION_LIST *p_section, char *sname, char *stitle, char *master_list)
1566 {
1567 if (p_section == NULL)
1568 {
1569 log_error("NULL pointer error\n");
1570 return -1;
1571 }
1572
1573 if (section_list_rd_lock(p_section) < 0)
1574 {
1575 log_error("section_list_rd_lock(sid=%d) error\n", p_section->sid);
1576 return -2;
1577 }
1578
1579 if (sname != NULL)
1580 {
1581 memcpy(sname, p_section->sname, sizeof(p_section->sname));
1582 }
1583 if (stitle != NULL)
1584 {
1585 memcpy(stitle, p_section->stitle, sizeof(p_section->stitle));
1586 }
1587 if (master_list != NULL)
1588 {
1589 memcpy(master_list, p_section->master_list, sizeof(p_section->master_list));
1590 }
1591
1592 // release lock of section
1593 if (section_list_rd_unlock(p_section) < 0)
1594 {
1595 log_error("section_list_rd_unlock(sid=%d) error\n", p_section->sid);
1596 return -2;
1597 }
1598
1599 return 0;
1600 }
1601
1602 int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1603 {
1604 int index;
1605 struct sembuf sops[4];
1606 struct timespec timeout;
1607 int ret;
1608
1609 index = get_section_index(p_section);
1610 if (index < 0)
1611 {
1612 return -2;
1613 }
1614
1615 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1616 sops[0].sem_op = 0; // wait until unlocked
1617 sops[0].sem_flg = 0;
1618
1619 sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1620 sops[1].sem_op = 1; // lock
1621 sops[1].sem_flg = SEM_UNDO; // undo on terminate
1622
1623 // Read lock on any specific section will also acquire single read lock on "all section"
1624 // so that write lock on all section only need to acquire single write on on "all section"
1625 // rather than to acquire multiple write locks on all the available sections.
1626 if (index != BBS_max_section)
1627 {
1628 sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1629 sops[2].sem_op = 0; // wait until unlocked
1630 sops[2].sem_flg = 0;
1631
1632 sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1633 sops[3].sem_op = 1; // lock
1634 sops[3].sem_flg = SEM_UNDO; // undo on terminate
1635 }
1636
1637 timeout.tv_sec = wait_sec;
1638 timeout.tv_nsec = 0;
1639
1640 ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1641 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1642 {
1643 log_error("semtimedop(index = %d, lock read) error %d\n", index, errno);
1644 }
1645
1646 return ret;
1647 }
1648
1649 int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1650 {
1651 int index;
1652 struct sembuf sops[3];
1653 struct timespec timeout;
1654 int ret;
1655
1656 index = get_section_index(p_section);
1657 if (index < 0)
1658 {
1659 return -2;
1660 }
1661
1662 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1663 sops[0].sem_op = 0; // wait until unlocked
1664 sops[0].sem_flg = 0;
1665
1666 sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1667 sops[1].sem_op = 1; // lock
1668 sops[1].sem_flg = SEM_UNDO; // undo on terminate
1669
1670 sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1671 sops[2].sem_op = 0; // wait until unlocked
1672 sops[2].sem_flg = 0;
1673
1674 timeout.tv_sec = wait_sec;
1675 timeout.tv_nsec = 0;
1676
1677 ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1678 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1679 {
1680 log_error("semtimedop(index = %d, lock write) error %d\n", index, errno);
1681 }
1682
1683 return ret;
1684 }
1685
1686 int section_list_rd_unlock(SECTION_LIST *p_section)
1687 {
1688 int index;
1689 struct sembuf sops[2];
1690 int ret;
1691
1692 index = get_section_index(p_section);
1693 if (index < 0)
1694 {
1695 return -2;
1696 }
1697
1698 sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1699 sops[0].sem_op = -1; // unlock
1700 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1701
1702 if (index != BBS_max_section)
1703 {
1704 sops[1].sem_num = BBS_max_section * 2; // r_sem of all section
1705 sops[1].sem_op = -1; // unlock
1706 sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1707 }
1708
1709 ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1710 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1711 {
1712 log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1713 }
1714
1715 return ret;
1716 }
1717
1718 int section_list_rw_unlock(SECTION_LIST *p_section)
1719 {
1720 int index;
1721 struct sembuf sops[1];
1722 int ret;
1723
1724 index = get_section_index(p_section);
1725 if (index < 0)
1726 {
1727 return -2;
1728 }
1729
1730 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1731 sops[0].sem_op = -1; // unlock
1732 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1733
1734 ret = semop(p_section_list_pool->semid, sops, 1);
1735 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1736 {
1737 log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1738 }
1739
1740 return ret;
1741 }
1742
1743 int section_list_rd_lock(SECTION_LIST *p_section)
1744 {
1745 int timer = 0;
1746 int sid = (p_section == NULL ? 0 : p_section->sid);
1747 int ret = -1;
1748
1749 while (!SYS_server_exit)
1750 {
1751 ret = section_list_try_rd_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1752 if (ret == 0) // success
1753 {
1754 break;
1755 }
1756 else if (errno == EAGAIN || errno == EINTR) // retry
1757 {
1758 timer++;
1759 if (timer % SECTION_TRY_LOCK_TIMES == 0)
1760 {
1761 log_error("section_list_try_rd_lock() tried %d times on section %d\n", timer, sid);
1762 }
1763 }
1764 else // failed
1765 {
1766 log_error("section_list_try_rd_lock() failed on section %d\n", sid);
1767 break;
1768 }
1769 }
1770
1771 return ret;
1772 }
1773
1774 int section_list_rw_lock(SECTION_LIST *p_section)
1775 {
1776 int timer = 0;
1777 int sid = (p_section == NULL ? 0 : p_section->sid);
1778 int ret = -1;
1779
1780 while (!SYS_server_exit)
1781 {
1782 ret = section_list_try_rw_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1783 if (ret == 0) // success
1784 {
1785 break;
1786 }
1787 else if (errno == EAGAIN || errno == EINTR) // retry
1788 {
1789 timer++;
1790 if (timer % SECTION_TRY_LOCK_TIMES == 0)
1791 {
1792 log_error("section_list_try_rw_lock() tried %d times on section %d\n", timer, sid);
1793 }
1794 }
1795 else // failed
1796 {
1797 log_error("section_list_try_rw_lock() failed on section %d\n", sid);
1798 break;
1799 }
1800 }
1801
1802 return ret;
1803 }

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