/[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.59 - (show annotations)
Mon Nov 17 12:16:48 2025 UTC (3 months, 4 weeks ago) by sysadm
Branch: MAIN
Changes since 1.58: +18 -0 lines
Content type: text/x-csrc
Add alternative implementation of shmat(..., SHM_REMAP) under MSYS2
Fix error reported by gcc under MSYS2

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

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