/[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.63 - (show annotations)
Thu Nov 20 10:20:51 2025 UTC (3 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.62: +221 -23 lines
Content type: text/x-csrc
Add alternative POSIX semaphore based rd/rw (un)lock in section_list

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

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