/[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.45 - (show annotations)
Tue Oct 21 06:24:51 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.44: +2 -2 lines
Content type: text/x-csrc
Add user_list with data loader

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

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