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

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