/[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.66 - (show annotations)
Fri Dec 19 06:16:27 2025 UTC (2 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.65: +132 -132 lines
Content type: text/x-csrc
Append \n to the end of logging message by log_...()
Remove ending \n from each logging message

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

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