/[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.37 - (show annotations)
Wed Jun 25 02:49:20 2025 UTC (8 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.36: +4 -4 lines
Content type: text/x-csrc
Use RW_lock to avoid conflict between menu reload of data loader process and main process in rare condition

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

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