/[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.22 - (show annotations)
Sun May 25 23:47:36 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.21: +98 -82 lines
Content type: text/x-csrc
Move global variables to SHM

1 /***************************************************************************
2 section_list.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #define _GNU_SOURCE
18
19 #include "section_list.h"
20 #include "log.h"
21 #include "trie_dict.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <signal.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <sys/param.h>
29 #include <sys/sem.h>
30 #include <sys/shm.h>
31 #include <sys/ipc.h>
32
33 #ifdef _SEM_SEMUN_UNDEFINED
34 union semun
35 {
36 int val; /* Value for SETVAL */
37 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
38 unsigned short *array; /* Array for GETALL, SETALL */
39 struct seminfo *__buf; /* Buffer for IPC_INFO
40 (Linux-specific) */
41 };
42 #endif // #ifdef _SEM_SEMUN_UNDEFINED
43
44 #define ARTICLE_BLOCK_PER_SHM 400 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
45 #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 200 // limited by length (8-bit) of proj_id in ftok(path, proj_id)
46 #define ARTICLE_BLOCK_PER_POOL (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)
47
48 #define CALCULATE_PAGE_THRESHOLD 100 // Adjust to tune performance of move topic
49
50 #define SID_STR_LEN 5 // 32-bit + NULL
51
52 struct article_block_t
53 {
54 ARTICLE articles[ARTICLE_PER_BLOCK];
55 int article_count;
56 struct article_block_t *p_next_block;
57 };
58 typedef struct article_block_t ARTICLE_BLOCK;
59
60 struct article_block_pool_t
61 {
62 int shmid;
63 struct
64 {
65 int shmid;
66 void *p_shm;
67 } shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
68 int shm_count;
69 ARTICLE_BLOCK *p_block_free_list;
70 ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
71 int block_count;
72 };
73 typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
74
75 static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
76
77 struct section_list_pool_t
78 {
79 int shmid;
80 SECTION_LIST sections[BBS_max_section];
81 int section_count;
82 int semid;
83 TRIE_NODE *p_trie_dict_section_by_name;
84 TRIE_NODE *p_trie_dict_section_by_sid;
85 };
86 typedef struct section_list_pool_t SECTION_LIST_POOL;
87
88 static SECTION_LIST_POOL *p_section_list_pool = NULL;
89
90 int article_block_init(const char *filename, int block_count)
91 {
92 int shmid;
93 int proj_id;
94 key_t key;
95 size_t size;
96 void *p_shm;
97 int i;
98 int block_count_in_shm;
99 ARTICLE_BLOCK *p_block_in_shm;
100 ARTICLE_BLOCK **pp_block_next;
101
102 if (p_article_block_pool != NULL)
103 {
104 log_error("article_block_pool already initialized\n");
105 return -1;
106 }
107
108 if (block_count > ARTICLE_BLOCK_PER_POOL)
109 {
110 log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
111 return -2;
112 }
113
114 // Allocate shared memory
115 proj_id = ARTICLE_BLOCK_SHM_COUNT_LIMIT; // keep different from proj_id used to create block shm
116 key = ftok(filename, proj_id);
117 if (key == -1)
118 {
119 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
120 return -3;
121 }
122
123 size = sizeof(ARTICLE_BLOCK_POOL);
124 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
125 if (shmid == -1)
126 {
127 log_error("shmget(article_block_pool_shm, size = %d) error (%d)\n", size, errno);
128 return -3;
129 }
130 p_shm = shmat(shmid, NULL, 0);
131 if (p_shm == (void *)-1)
132 {
133 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
134 return -3;
135 }
136
137 p_article_block_pool = p_shm;
138 p_article_block_pool->shmid = shmid;
139
140 p_article_block_pool->shm_count = 0;
141 pp_block_next = &(p_article_block_pool->p_block_free_list);
142
143 while (block_count > 0)
144 {
145 block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
146 block_count -= block_count_in_shm;
147
148 proj_id = p_article_block_pool->shm_count;
149 key = ftok(filename, proj_id);
150 if (key == -1)
151 {
152 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
153 article_block_cleanup();
154 return -3;
155 }
156
157 size = sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
158 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
159 if (shmid == -1)
160 {
161 log_error("shmget(shm_index = %d, size = %d) error (%d)\n", p_article_block_pool->shm_count, size, errno);
162 article_block_cleanup();
163 return -3;
164 }
165 p_shm = shmat(shmid, NULL, 0);
166 if (p_shm == (void *)-1)
167 {
168 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
169 article_block_cleanup();
170 return -3;
171 }
172
173 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->shmid = shmid;
174 (p_article_block_pool->shm_pool + p_article_block_pool->shm_count)->p_shm = p_shm;
175 p_article_block_pool->shm_count++;
176
177 p_block_in_shm = p_shm;
178 *pp_block_next = p_block_in_shm;
179
180 for (i = 0; i < block_count_in_shm; i++)
181 {
182 if (i < block_count_in_shm - 1)
183 {
184 (p_block_in_shm + i)->p_next_block = (p_block_in_shm + i + 1);
185 }
186 else
187 {
188 (p_block_in_shm + i)->p_next_block = NULL;
189 pp_block_next = &((p_block_in_shm + i)->p_next_block);
190 }
191 }
192 }
193
194 p_article_block_pool->block_count = 0;
195
196 return 0;
197 }
198
199 void article_block_cleanup(void)
200 {
201 int shmid;
202
203 if (p_article_block_pool == NULL)
204 {
205 return;
206 }
207
208 for (int i = 0; i < p_article_block_pool->shm_count; i++)
209 {
210 if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
211 {
212 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
213 }
214
215 if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
216 {
217 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
218 }
219 }
220
221 shmid = p_article_block_pool->shmid;
222
223 if (shmdt(p_article_block_pool) == -1)
224 {
225 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
226 }
227
228 if (shmctl(shmid, IPC_RMID, NULL) == -1)
229 {
230 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
231 }
232
233 p_article_block_pool = NULL;
234 }
235
236 inline static ARTICLE_BLOCK *pop_free_article_block(void)
237 {
238 ARTICLE_BLOCK *p_block = NULL;
239
240 if (p_article_block_pool->p_block_free_list != NULL)
241 {
242 p_block = p_article_block_pool->p_block_free_list;
243 p_article_block_pool->p_block_free_list = p_block->p_next_block;
244 p_block->p_next_block = NULL;
245 p_block->article_count = 0;
246 }
247
248 return p_block;
249 }
250
251 inline static void push_free_article_block(ARTICLE_BLOCK *p_block)
252 {
253 p_block->p_next_block = p_article_block_pool->p_block_free_list;
254 p_article_block_pool->p_block_free_list = p_block;
255 }
256
257 int article_block_reset(void)
258 {
259 ARTICLE_BLOCK *p_block;
260
261 if (p_article_block_pool == NULL)
262 {
263 log_error("article_block_pool not initialized\n");
264 return -1;
265 }
266
267 while (p_article_block_pool->block_count > 0)
268 {
269 p_article_block_pool->block_count--;
270 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count];
271 push_free_article_block(p_block);
272 }
273
274 return 0;
275 }
276
277 ARTICLE *article_block_find_by_aid(int32_t aid)
278 {
279 ARTICLE_BLOCK *p_block;
280 int left;
281 int right;
282 int mid;
283
284 if (p_article_block_pool == NULL)
285 {
286 log_error("article_block_pool not initialized\n");
287 return NULL;
288 }
289
290 if (p_article_block_pool->block_count == 0) // empty
291 {
292 return NULL;
293 }
294
295 left = 0;
296 right = p_article_block_pool->block_count;
297
298 // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]
299 while (left < right - 1)
300 {
301 // get block offset no less than mid value of left and right block offsets
302 mid = (left + right) / 2 + (right - left) % 2;
303
304 if (mid >= p_article_block_pool->block_count)
305 {
306 log_error("block(mid = %d) is out of boundary\n", mid);
307 return NULL;
308 }
309
310 if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
311 {
312 right = mid;
313 }
314 else
315 {
316 left = mid;
317 }
318 }
319
320 p_block = p_article_block_pool->p_block[left];
321
322 left = 0;
323 right = p_block->article_count - 1;
324
325 // aid in the range [ aid of articles[left], aid of articles[right] ]
326 while (left < right)
327 {
328 mid = (left + right) / 2;
329
330 if (aid <= p_block->articles[mid].aid)
331 {
332 right = mid;
333 }
334 else
335 {
336 left = mid + 1;
337 }
338 }
339
340 return (p_block->articles + left);
341 }
342
343 ARTICLE *article_block_find_by_index(int index)
344 {
345 ARTICLE_BLOCK *p_block;
346
347 if (p_article_block_pool == NULL)
348 {
349 log_error("article_block_pool not initialized\n");
350 return NULL;
351 }
352
353 if (index < 0 || index / ARTICLE_PER_BLOCK >= p_article_block_pool->block_count)
354 {
355 log_error("article_block_find_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
356 return NULL;
357 }
358
359 p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];
360
361 if (index % ARTICLE_PER_BLOCK >= p_block->article_count)
362 {
363 log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
364 return NULL;
365 }
366
367 return (p_block->articles + (index % ARTICLE_PER_BLOCK));
368 }
369
370 extern int section_list_init(const char *filename)
371 {
372 int semid;
373 int shmid;
374 int proj_id;
375 key_t key;
376 size_t size;
377 void *p_shm;
378 union semun arg;
379 int i;
380
381 if (p_section_list_pool != NULL)
382 {
383 section_list_cleanup();
384 }
385
386 proj_id = (int)(time(NULL) % getpid());
387 key = ftok(filename, proj_id);
388 if (key == -1)
389 {
390 log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
391 return -3;
392 }
393
394 // Allocate shared memory
395 size = sizeof(SECTION_LIST_POOL);
396 shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
397 if (shmid == -1)
398 {
399 log_error("shmget(section_list_pool_shm, size = %d) error (%d)\n", size, errno);
400 return -3;
401 }
402 p_shm = shmat(shmid, NULL, 0);
403 if (p_shm == (void *)-1)
404 {
405 log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
406 return -3;
407 }
408
409 p_section_list_pool = p_shm;
410 p_section_list_pool->shmid = shmid;
411 p_section_list_pool->section_count = 0;
412
413 // Allocate semaphore as section locks
414 size = 2 * (BBS_max_section + 1); // r_sem and w_sem per section, the last pair for all sections
415 semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
416 if (semid == -1)
417 {
418 log_error("semget(section_list_pool_sem, size = %d) error (%d)\n", size, errno);
419 return -3;
420 }
421
422 // Initialize sem value to 0
423 arg.val = 0;
424 for (i = 0; i < size; i++)
425 {
426 if (semctl(semid, i, SETVAL, arg) == -1)
427 {
428 log_error("semctl(section_list_pool_sem, SETVAL) error (%d)\n", errno);
429 return -3;
430 }
431 }
432
433 p_section_list_pool->semid = semid;
434
435 p_section_list_pool->p_trie_dict_section_by_name = trie_dict_create();
436 if (p_section_list_pool->p_trie_dict_section_by_name == NULL)
437 {
438 log_error("trie_dict_create() OOM\n", BBS_max_section);
439 return -2;
440 }
441
442 p_section_list_pool->p_trie_dict_section_by_sid = trie_dict_create();
443 if (p_section_list_pool->p_trie_dict_section_by_sid == NULL)
444 {
445 log_error("trie_dict_create() OOM\n", BBS_max_section);
446 return -2;
447 }
448
449 return 0;
450 }
451
452 inline static void sid_to_str(int32_t sid, char *p_sid_str)
453 {
454 uint32_t u_sid;
455 int i;
456
457 u_sid = (uint32_t)sid;
458 for (i = 0; i < SID_STR_LEN - 1; i++)
459 {
460 p_sid_str[i] = (char)(u_sid % 255 + 1);
461 u_sid /= 255;
462 }
463 p_sid_str[i] = '\0';
464 }
465
466 SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_name)
467 {
468 SECTION_LIST *p_section;
469 char sid_str[SID_STR_LEN];
470
471 if (p_section_list_pool == NULL)
472 {
473 log_error("session_list_pool not initialized\n");
474 return NULL;
475 }
476
477 if (p_section_list_pool->section_count >= BBS_max_section)
478 {
479 log_error("section_count reach limit %d >= %d\n", p_section_list_pool->section_count, BBS_max_section);
480 return NULL;
481 }
482
483 sid_to_str(sid, sid_str);
484
485 p_section = p_section_list_pool->sections + p_section_list_pool->section_count;
486
487 p_section->sid = sid;
488
489 strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));
490 p_section->sname[sizeof(p_section->sname - 1)] = '\0';
491
492 strncpy(p_section->stitle, stitle, sizeof(p_section->stitle - 1));
493 p_section->stitle[sizeof(p_section->stitle - 1)] = '\0';
494
495 strncpy(p_section->master_name, master_name, sizeof(p_section->master_name - 1));
496 p_section->master_name[sizeof(p_section->master_name - 1)] = '\0';
497
498 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, p_section_list_pool->section_count) != 1)
499 {
500 log_error("trie_dict_set(section, %s, %d) error\n", sname, p_section_list_pool->section_count);
501 return NULL;
502 }
503
504 if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, p_section_list_pool->section_count) != 1)
505 {
506 log_error("trie_dict_set(section, %d, %d) error\n", sid, p_section_list_pool->section_count);
507 return NULL;
508 }
509
510 section_list_reset_articles(p_section);
511
512 p_section_list_pool->section_count++;
513
514 return p_section;
515 }
516
517 void section_list_reset_articles(SECTION_LIST *p_section)
518 {
519 p_section->article_count = 0;
520 p_section->topic_count = 0;
521 p_section->visible_article_count = 0;
522 p_section->visible_topic_count = 0;
523 p_section->p_article_head = NULL;
524 p_section->p_article_tail = NULL;
525
526 p_section->page_count = 0;
527 p_section->last_page_visible_article_count = 0;
528 }
529
530 void section_list_cleanup(void)
531 {
532 int shmid;
533
534 if (p_section_list_pool == NULL)
535 {
536 return;
537 }
538
539 if (p_section_list_pool->p_trie_dict_section_by_name != NULL)
540 {
541 trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_name);
542 p_section_list_pool->p_trie_dict_section_by_name = NULL;
543 }
544
545 if (p_section_list_pool->p_trie_dict_section_by_sid != NULL)
546 {
547 trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_sid);
548 p_section_list_pool->p_trie_dict_section_by_sid = NULL;
549 }
550
551 if (p_section_list_pool != NULL)
552 {
553 if (semctl(p_section_list_pool->semid, 0, IPC_RMID) == -1)
554 {
555 log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_section_list_pool->semid, errno);
556 }
557
558 shmid = p_section_list_pool->shmid;
559
560 if (shmdt(p_section_list_pool) == -1)
561 {
562 log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
563 }
564
565 if (shmctl(shmid, IPC_RMID, NULL) == -1)
566 {
567 log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
568 }
569
570 p_section_list_pool = NULL;
571 }
572 }
573
574 SECTION_LIST *section_list_find_by_name(const char *sname)
575 {
576 int64_t index;
577
578 if (p_section_list_pool == NULL)
579 {
580 log_error("section_list not initialized\n");
581 return NULL;
582 }
583
584 if (trie_dict_get(p_section_list_pool->p_trie_dict_section_by_name, sname, &index) != 1)
585 {
586 log_error("trie_dict_get(section, %s) error\n", sname);
587 return NULL;
588 }
589
590 return (p_section_list_pool->sections + index);
591 }
592
593 SECTION_LIST *section_list_find_by_sid(int32_t sid)
594 {
595 int64_t index;
596 char sid_str[SID_STR_LEN];
597
598 if (p_section_list_pool == NULL)
599 {
600 log_error("section_list not initialized\n");
601 return NULL;
602 }
603
604 sid_to_str(sid, sid_str);
605
606 if (trie_dict_get(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, &index) != 1)
607 {
608 log_error("trie_dict_get(section, %d) error\n", sid);
609 return NULL;
610 }
611
612 return (p_section_list_pool->sections + index);
613 }
614
615 int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
616 {
617 ARTICLE_BLOCK *p_block;
618 int32_t last_aid = 0;
619 ARTICLE *p_article;
620 ARTICLE *p_topic_head;
621 ARTICLE *p_topic_tail;
622
623 if (p_section == NULL || p_article_src == NULL)
624 {
625 log_error("section_list_append_article() NULL pointer error\n");
626 return -1;
627 }
628
629 if (p_article_block_pool == NULL)
630 {
631 log_error("article_block_pool not initialized\n");
632 return -1;
633 }
634
635 if (p_section->sid != p_article_src->sid)
636 {
637 log_error("section_list_append_article() error: section sid %d != article sid %d\n", p_section->sid, p_article_src->sid);
638 return -2;
639 }
640
641 if (p_section->article_count >= BBS_article_limit_per_section)
642 {
643 log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
644 return -2;
645 }
646
647 if (p_article_block_pool->block_count == 0 ||
648 p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)
649 {
650 if ((p_block = pop_free_article_block()) == NULL)
651 {
652 log_error("pop_free_article_block() error\n");
653 return -2;
654 }
655
656 if (p_article_block_pool->block_count > 0)
657 {
658 last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;
659 }
660
661 p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
662 p_article_block_pool->block_count++;
663 }
664 else
665 {
666 p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
667 last_aid = p_block->articles[p_block->article_count - 1].aid;
668 }
669
670 // AID of articles should be strictly ascending
671 if (p_article_src->aid <= last_aid)
672 {
673 log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
674 return -3;
675 }
676
677 p_article = (p_block->articles + p_block->article_count);
678 p_block->article_count++;
679 p_section->article_count++;
680
681 // Copy article data
682 *p_article = *p_article_src;
683
684 if (p_article->visible)
685 {
686 p_section->visible_article_count++;
687 }
688
689 // Link appended article as tail node of topic bi-directional list
690 if (p_article->tid != 0)
691 {
692 p_topic_head = article_block_find_by_aid(p_article->tid);
693 if (p_topic_head == NULL)
694 {
695 log_error("search head of topic (aid=%d) error\n", p_article->tid);
696 return -4;
697 }
698
699 p_topic_tail = p_topic_head->p_topic_prior;
700 if (p_topic_tail == NULL)
701 {
702 log_error("tail of topic (aid=%d) is NULL\n", p_article->tid);
703 return -4;
704 }
705 }
706 else
707 {
708 p_section->topic_count++;
709
710 if (p_article->visible)
711 {
712 p_section->visible_topic_count++;
713 }
714
715 p_topic_head = p_article;
716 p_topic_tail = p_article;
717 }
718
719 p_article->p_topic_prior = p_topic_tail;
720 p_article->p_topic_next = p_topic_head;
721 p_topic_head->p_topic_prior = p_article;
722 p_topic_tail->p_topic_next = p_article;
723
724 // Link appended article as tail node of article bi-directional list
725 if (p_section->p_article_head == NULL)
726 {
727 p_section->p_article_head = p_article;
728 p_section->p_article_tail = p_article;
729 }
730 p_article->p_prior = p_section->p_article_tail;
731 p_article->p_next = p_section->p_article_head;
732 p_section->p_article_head->p_prior = p_article;
733 p_section->p_article_tail->p_next = p_article;
734 p_section->p_article_tail = p_article;
735
736 // Update page
737 if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||
738 p_section->article_count == 1)
739 {
740 p_section->p_page_first_article[p_section->page_count] = p_article;
741 p_section->page_count++;
742 p_section->last_page_visible_article_count = 0;
743 }
744
745 if (p_article->visible)
746 {
747 p_section->last_page_visible_article_count++;
748 }
749
750 return 0;
751 }
752
753 int section_list_set_article_visible(SECTION_LIST *p_section, int32_t aid, int8_t visible)
754 {
755 ARTICLE *p_article;
756 ARTICLE *p_reply;
757 int affected_count = 0;
758
759 if (p_section == NULL)
760 {
761 log_error("section_list_set_article_visible() NULL pointer error\n");
762 return -2;
763 }
764
765 p_article = article_block_find_by_aid(aid);
766 if (p_article == NULL)
767 {
768 return -1; // Not found
769 }
770
771 if (p_section->sid != p_article->sid)
772 {
773 log_error("section_list_set_article_visible() error: section sid %d != article sid %d\n", p_section->sid, p_article->sid);
774 return -2;
775 }
776
777 if (p_article->visible == visible)
778 {
779 return 0; // Already set
780 }
781
782 if (visible == 0) // 1 -> 0
783 {
784 p_section->visible_article_count--;
785
786 if (p_article->tid == 0)
787 {
788 p_section->visible_topic_count--;
789
790 // Set related visible replies to invisible
791 for (p_reply = p_article->p_topic_next; p_reply->tid != 0; p_reply = p_reply->p_topic_next)
792 {
793 if (p_reply->tid != aid)
794 {
795 log_error("Inconsistent tid = %d found in reply %d of topic %d\n", p_reply->tid, p_reply->aid, aid);
796 continue;
797 }
798
799 if (p_reply->visible == 1)
800 {
801 p_reply->visible = 0;
802 p_section->visible_article_count--;
803 affected_count++;
804 }
805 }
806 }
807 }
808 else // 0 -> 1
809 {
810 p_section->visible_article_count++;
811
812 if (p_article->tid == 0)
813 {
814 p_section->visible_topic_count++;
815 }
816 }
817
818 p_article->visible = visible;
819 affected_count++;
820
821 return affected_count;
822 }
823
824 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)
825 {
826 ARTICLE *p_article;
827 int left;
828 int right;
829 int mid;
830
831 *p_page = -1;
832 *p_offset = -1;
833 *pp_next = NULL;
834
835 if (p_section == NULL)
836 {
837 log_error("section_list_find_article_with_offset() NULL pointer error\n");
838 return NULL;
839 }
840
841 if (p_section->article_count == 0) // empty
842 {
843 *p_page = 0;
844 *p_offset = 0;
845 return NULL;
846 }
847
848 left = 0;
849 right = p_section->page_count;
850
851 // aid in the range [ head aid of pages[left], tail aid of pages[right - 1] ]
852 while (left < right - 1)
853 {
854 // get page id no less than mid value of left page id and right page id
855 mid = (left + right) / 2 + (right - left) % 2;
856
857 if (mid >= p_section->page_count)
858 {
859 log_error("page id (mid = %d) is out of boundary\n", mid);
860 return NULL;
861 }
862
863 if (aid < p_section->p_page_first_article[mid]->aid)
864 {
865 right = mid;
866 }
867 else
868 {
869 left = mid;
870 }
871 }
872
873 *p_page = left;
874
875 p_article = p_section->p_page_first_article[*p_page];
876
877 // p_section->p_page_first_article[*p_page]->aid <= aid < p_section->p_page_first_article[*p_page + 1]->aid
878 right = (*p_page == MAX(0, p_section->page_count - 1) ? INT32_MAX : p_section->p_page_first_article[*p_page + 1]->aid);
879
880 // left will be the offset of article found or offset to insert
881 left = 0;
882
883 while (aid > p_article->aid)
884 {
885 p_article = p_article->p_next;
886 left++;
887
888 if (aid == p_article->aid)
889 {
890 *pp_next = p_article->p_next;
891 break;
892 }
893
894 // over last article in the page
895 if (p_article == p_section->p_article_head || p_article->aid >= right)
896 {
897 *pp_next = (p_article == p_section->p_article_head ? p_section->p_article_head : p_section->p_page_first_article[*p_page + 1]);
898 *p_offset = left;
899 return NULL; // not found
900 }
901 }
902
903 if (aid < p_article->aid)
904 {
905 *pp_next = p_article;
906 p_article = NULL; // not found
907 }
908 else // aid == p_article->aid
909 {
910 *pp_next = p_article->p_next;
911 }
912
913 *p_offset = left;
914
915 return p_article;
916 }
917
918 int section_list_calculate_page(SECTION_LIST *p_section, int32_t start_aid)
919 {
920 ARTICLE *p_article;
921 ARTICLE *p_next;
922 int32_t page;
923 int32_t offset;
924 int visible_article_count;
925 int page_head_set;
926
927 if (p_section == NULL)
928 {
929 log_error("section_list_calculate_page() NULL pointer error\n");
930 return -1;
931 }
932
933 if (p_section->article_count == 0) // empty
934 {
935 p_section->page_count = 0;
936 p_section->last_page_visible_article_count = 0;
937
938 return 0;
939 }
940
941 if (start_aid > 0)
942 {
943 p_article = article_block_find_by_aid(start_aid);
944 if (p_article == NULL)
945 {
946 return -1; // Not found
947 }
948
949 if (p_section->sid != p_article->sid)
950 {
951 log_error("section_list_calculate_page() error: section sid %d != start article sid %d\n", p_section->sid, p_article->sid);
952 return -2;
953 }
954
955 p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
956 if (p_article == NULL)
957 {
958 if (page < 0)
959 {
960 return -1;
961 }
962 log_error("section_list_calculate_page() aid = %d not found in section sid = %d\n",
963 start_aid, p_section->sid);
964 return -2;
965 }
966
967 if (offset > 0)
968 {
969 p_article = p_section->p_page_first_article[page];
970 }
971 }
972 else
973 {
974 p_article = p_section->p_article_head;
975 page = 0;
976 offset = 0;
977 }
978
979 visible_article_count = 0;
980 page_head_set = 0;
981
982 do
983 {
984 if (!page_head_set && visible_article_count == 0)
985 {
986 p_section->p_page_first_article[page] = p_article;
987 page_head_set = 1;
988 }
989
990 if (p_article->visible)
991 {
992 visible_article_count++;
993 }
994
995 p_article = p_article->p_next;
996
997 // skip remaining invisible articles
998 while (p_article->visible == 0 && p_article != p_section->p_article_head)
999 {
1000 p_article = p_article->p_next;
1001 }
1002
1003 if (visible_article_count >= BBS_article_limit_per_page && p_article != p_section->p_article_head)
1004 {
1005 page++;
1006 visible_article_count = 0;
1007 page_head_set = 0;
1008
1009 if (page >= BBS_article_limit_per_section / BBS_article_limit_per_page && p_article != p_section->p_article_head)
1010 {
1011 log_error("Count of page exceed limit in section %d\n", p_section->sid);
1012 break;
1013 }
1014 }
1015 } while (p_article != p_section->p_article_head);
1016
1017 p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
1018 p_section->last_page_visible_article_count = visible_article_count;
1019
1020 return 0;
1021 }
1022
1023 int article_count_of_topic(int32_t aid)
1024 {
1025 ARTICLE *p_article;
1026 int article_count;
1027
1028 p_article = article_block_find_by_aid(aid);
1029 if (p_article == NULL)
1030 {
1031 return 0; // Not found
1032 }
1033
1034 article_count = 0;
1035
1036 do
1037 {
1038 if (p_article->tid != 0 && p_article->tid != aid)
1039 {
1040 log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1041 break;
1042 }
1043
1044 article_count++;
1045 p_article = p_article->p_topic_next;
1046 } while (p_article->aid != aid);
1047
1048 return article_count;
1049 }
1050
1051 int section_list_move_topic(SECTION_LIST *p_section_src, SECTION_LIST *p_section_dest, int32_t aid)
1052 {
1053 ARTICLE *p_article;
1054 ARTICLE *p_next;
1055 int32_t page;
1056 int32_t offset;
1057 int32_t move_article_count;
1058 int32_t dest_article_count_old;
1059 int32_t last_unaffected_aid_src;
1060 int32_t first_inserted_aid_dest;
1061 int move_counter;
1062
1063 if (p_section_dest == NULL)
1064 {
1065 log_error("section_list_move_topic() NULL pointer error\n");
1066 return -1;
1067 }
1068
1069 if ((p_article = article_block_find_by_aid(aid)) == NULL)
1070 {
1071 log_error("section_list_move_topic() error: article %d not found in block\n", aid);
1072 return -2;
1073 }
1074
1075 if (p_section_src->sid != p_article->sid)
1076 {
1077 log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1078 p_section_src->sid, p_article->aid, p_article->sid);
1079 return -2;
1080 }
1081
1082 if (p_article->tid != 0)
1083 {
1084 log_error("section_list_move_topic(aid = %d) error: article is not head of topic, tid = %d\n", aid, p_article->tid);
1085 return -2;
1086 }
1087
1088 last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1089
1090 move_article_count = article_count_of_topic(aid);
1091 if (move_article_count <= 0)
1092 {
1093 log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1094 return -2;
1095 }
1096
1097 if (p_section_dest->article_count + move_article_count > BBS_article_limit_per_section)
1098 {
1099 log_error("section_list_move_topic() error: article_count %d reach limit in section %d\n",
1100 p_section_dest->article_count + move_article_count, p_section_dest->sid);
1101 return -3;
1102 }
1103
1104 dest_article_count_old = p_section_dest->article_count;
1105 move_counter = 0;
1106 first_inserted_aid_dest = p_article->aid;
1107
1108 do
1109 {
1110 if (p_section_src->sid != p_article->sid)
1111 {
1112 log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1113 p_section_src->sid, p_article->aid, p_article->sid);
1114 return -2;
1115 }
1116
1117 // Remove from bi-directional article list of src section
1118 if (p_section_src->p_article_head == p_article)
1119 {
1120 p_section_src->p_article_head = p_article->p_next;
1121 }
1122 if (p_section_src->p_article_tail == p_article)
1123 {
1124 p_section_src->p_article_tail = p_article->p_prior;
1125 }
1126 if (p_section_src->p_article_head == p_article) // || p_section_src->p_article_tail == p_article
1127 {
1128 p_section_src->p_article_head = NULL;
1129 p_section_src->p_article_tail = NULL;
1130 }
1131
1132 p_article->p_prior->p_next = p_article->p_next;
1133 p_article->p_next->p_prior = p_article->p_prior;
1134
1135 // Update sid of article
1136 p_article->sid = p_section_dest->sid;
1137
1138 if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1139 {
1140 log_error("section_list_move_topic() error: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1141 return -4;
1142 }
1143
1144 // Insert into bi-directional article list of dest section
1145 if (p_next == NULL) // empty section
1146 {
1147 p_section_dest->p_article_head = p_article;
1148 p_section_dest->p_article_tail = p_article;
1149 p_article->p_prior = p_article;
1150 p_article->p_next = p_article;
1151 }
1152 else
1153 {
1154 if (p_section_dest->p_article_head == p_next)
1155 {
1156 if (p_article->aid < p_next->aid)
1157 {
1158 p_section_dest->p_article_head = p_article;
1159 }
1160 else // p_article->aid > p_next->aid
1161 {
1162 p_section_dest->p_article_tail = p_article;
1163 }
1164 }
1165
1166 p_article->p_prior = p_next->p_prior;
1167 p_article->p_next = p_next;
1168 p_next->p_prior->p_next = p_article;
1169 p_next->p_prior = p_article;
1170 }
1171
1172 // Update article / topic counter of src / desc section
1173 p_section_src->article_count--;
1174 p_section_dest->article_count++;
1175 if (p_article->tid == 0)
1176 {
1177 p_section_src->topic_count--;
1178 p_section_dest->topic_count++;
1179 }
1180
1181 // Update visible article / topic counter of src / desc section
1182 if (p_article->visible)
1183 {
1184 p_section_src->visible_article_count--;
1185 p_section_dest->visible_article_count++;
1186 if (p_article->tid == 0)
1187 {
1188 p_section_src->visible_topic_count--;
1189 p_section_dest->visible_topic_count++;
1190 }
1191 }
1192
1193 // Update page for empty dest section
1194 if (p_section_dest->article_count == 1)
1195 {
1196 p_section_dest->p_page_first_article[0] = p_article;
1197 p_section_dest->page_count = 1;
1198 p_section_dest->last_page_visible_article_count = (p_article->visible ? 1 : 0);
1199 }
1200
1201 p_article = p_article->p_topic_next;
1202
1203 move_counter++;
1204 if (move_counter % CALCULATE_PAGE_THRESHOLD == 0)
1205 {
1206 // Re-calculate pages of desc section
1207 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1208 {
1209 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1210 p_section_dest->sid, first_inserted_aid_dest);
1211 }
1212
1213 first_inserted_aid_dest = p_article->aid;
1214 }
1215 } while (p_article->aid != aid);
1216
1217 if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1218 {
1219 log_error("section_list_move_topic() error: count of moved articles %d != %d\n",
1220 p_section_dest->article_count - dest_article_count_old, move_article_count);
1221 }
1222
1223 // Re-calculate pages of src section
1224 if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1225 {
1226 log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1227 p_section_src->sid, last_unaffected_aid_src, aid);
1228 }
1229
1230 if (move_counter % CALCULATE_PAGE_THRESHOLD != 0)
1231 {
1232 // Re-calculate pages of desc section
1233 if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1234 {
1235 log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1236 p_section_dest->sid, first_inserted_aid_dest);
1237 }
1238 }
1239
1240 return move_article_count;
1241 }
1242
1243 int get_section_index(SECTION_LIST *p_section)
1244 {
1245 int index;
1246
1247 if (p_section_list_pool == NULL)
1248 {
1249 log_error("get_section_index() error: uninitialized\n");
1250 return -1;
1251 }
1252
1253 if (p_section == NULL)
1254 {
1255 index = BBS_max_section;
1256 }
1257 else
1258 {
1259 index = (int)(p_section - p_section_list_pool->sections);
1260 if (index < 0 || index >= BBS_max_section)
1261 {
1262 log_error("get_section_index(%d) error: index out of range\n", index);
1263 return -2;
1264 }
1265 }
1266
1267 return index;
1268 }
1269
1270 int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1271 {
1272 int index;
1273 struct sembuf sops[4];
1274 struct timespec timeout;
1275 int ret;
1276
1277 index = get_section_index(p_section);
1278 if (index < 0)
1279 {
1280 return -2;
1281 }
1282
1283 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1284 sops[0].sem_op = 0; // wait until unlocked
1285 sops[0].sem_flg = 0;
1286
1287 sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1288 sops[1].sem_op = 1; // lock
1289 sops[1].sem_flg = SEM_UNDO; // undo on terminate
1290
1291 // Read lock on any specific section will also acquire single read lock on "all section"
1292 // so that write lock on all section only need to acquire single write on on "all section"
1293 // rather than to acquire multiple write locks on all the available sections.
1294 if (index != BBS_max_section)
1295 {
1296 sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1297 sops[2].sem_op = 0; // wait until unlocked
1298 sops[2].sem_flg = 0;
1299
1300 sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1301 sops[3].sem_op = 1; // lock
1302 sops[3].sem_flg = SEM_UNDO; // undo on terminate
1303 }
1304
1305 timeout.tv_sec = wait_sec;
1306 timeout.tv_nsec = 0;
1307
1308 ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1309 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1310 {
1311 log_error("semtimedop(index = %d, lock read) error %d\n", index, errno);
1312 }
1313
1314 return ret;
1315 }
1316
1317 int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1318 {
1319 int index;
1320 struct sembuf sops[3];
1321 struct timespec timeout;
1322 int ret;
1323
1324 index = get_section_index(p_section);
1325 if (index < 0)
1326 {
1327 return -2;
1328 }
1329
1330 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1331 sops[0].sem_op = 0; // wait until unlocked
1332 sops[0].sem_flg = 0;
1333
1334 sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1335 sops[1].sem_op = 1; // lock
1336 sops[1].sem_flg = SEM_UNDO; // undo on terminate
1337
1338 sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1339 sops[2].sem_op = 0; // wait until unlocked
1340 sops[2].sem_flg = 0;
1341
1342 timeout.tv_sec = wait_sec;
1343 timeout.tv_nsec = 0;
1344
1345 ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1346 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1347 {
1348 log_error("semtimedop(index = %d, lock write) error %d\n", index, errno);
1349 }
1350
1351 return ret;
1352 }
1353
1354 int section_list_rd_unlock(SECTION_LIST *p_section)
1355 {
1356 int index;
1357 struct sembuf sops[2];
1358 int ret;
1359
1360 index = get_section_index(p_section);
1361 if (index < 0)
1362 {
1363 return -2;
1364 }
1365
1366 sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1367 sops[0].sem_op = -1; // unlock
1368 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1369
1370 if (index != BBS_max_section)
1371 {
1372 sops[1].sem_num = BBS_max_section * 2; // r_sem of all section
1373 sops[1].sem_op = -1; // unlock
1374 sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1375 }
1376
1377 ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1378 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1379 {
1380 log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1381 }
1382
1383 return ret;
1384 }
1385
1386 int section_list_rw_unlock(SECTION_LIST *p_section)
1387 {
1388 int index;
1389 struct sembuf sops[1];
1390 int ret;
1391
1392 index = get_section_index(p_section);
1393 if (index < 0)
1394 {
1395 return -2;
1396 }
1397
1398 sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1399 sops[0].sem_op = -1; // unlock
1400 sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1401
1402 ret = semop(p_section_list_pool->semid, sops, 1);
1403 if (ret == -1 && errno != EAGAIN && errno != EINTR)
1404 {
1405 log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1406 }
1407
1408 return ret;
1409 }

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