/[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.37 - (hide annotations)
Wed Jun 25 02:49:20 2025 UTC (8 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.36: +4 -4 lines
Content type: text/x-csrc
Use RW_lock to avoid conflict between menu reload of data loader process and main process in rare condition

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

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