/[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.53 - (show annotations)
Tue Nov 4 14:58:56 2025 UTC (4 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.52: +1 -1 lines
Content type: text/x-csrc
Refine file header information comments

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

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