/[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.47 - (show annotations)
Fri Oct 24 02:08:19 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.46: +12 -24 lines
Content type: text/x-csrc
Refine search algorithm of section_list

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

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