/[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.16 - (hide annotations)
Sat May 24 07:32:46 2025 UTC (9 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.15: +151 -0 lines
Content type: text/x-csrc
Add read / write lock for sections

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

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