/[LeafOK_CVS]/lbbs/src/section_list.c
ViewVC logotype

Annotation of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.44 - (hide annotations)
Tue Oct 14 05:52:29 2025 UTC (5 months ago) by sysadm
Branch: MAIN
Changes since 1.43: +6 -2 lines
Content type: text/x-csrc
Fix bug

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

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