/[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.46 - (hide annotations)
Thu Oct 23 05:54:16 2025 UTC (4 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.45: +16 -0 lines
Content type: text/x-csrc
Update user_stat_article_cnt in section_list_set_article_visible()

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

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