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

Diff of /lbbs/src/section_list.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 1.12 by sysadm, Fri May 23 10:45:54 2025 UTC Revision 1.59 by sysadm, Mon Nov 17 12:16:48 2025 UTC
# Line 1  Line 1 
1  /***************************************************************************  /* SPDX-License-Identifier: GPL-3.0-or-later */
2                                             section_list.c  -  description  /*
3                                                           -------------------   * section_list
4          Copyright            : (C) 2004-2025 by Leaflet   *   - data models and basic operations of section and article
5          Email                : leaflet@leafok.com   *
6   ***************************************************************************/   * Copyright (C) 2004-2025  Leaflet <leaflet@leafok.com>
7     */
8  /***************************************************************************  
9   *                                                                         *  #ifdef HAVE_CONFIG_H
10   *   This program is free software; you can redistribute it and/or modify  *  #include "config.h"
11   *   it under the terms of the GNU General Public License as published by  *  #endif
  *   the Free Software Foundation; either version 3 of the License, or     *  
  *   (at your option) any later version.                                   *  
  *                                                                         *  
  ***************************************************************************/  
12    
 #include "section_list.h"  
13  #include "log.h"  #include "log.h"
14    #include "section_list.h"
15  #include "trie_dict.h"  #include "trie_dict.h"
16    #include "user_list.h"
17    #include <errno.h>
18    #include <signal.h>
19  #include <stdio.h>  #include <stdio.h>
20    #include <stdlib.h>
21  #include <string.h>  #include <string.h>
 #include <signal.h>  
22  #include <unistd.h>  #include <unistd.h>
23  #include <stdlib.h>  #include <sys/ipc.h>
 #include <errno.h>  
24  #include <sys/param.h>  #include <sys/param.h>
25    #include <sys/sem.h>
26  #include <sys/shm.h>  #include <sys/shm.h>
 #include <sys/ipc.h>  
27    
28  #define ARTICLE_BLOCK_PER_SHM 400                 // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate  #if defined(_SEM_SEMUN_UNDEFINED) || defined(__MSYS__) || defined(__MINGW32__)
29  #define ARTICLE_BLOCK_SHM_COUNT_LIMIT 256 // limited by length (8-bit) of proj_id in ftok(path, proj_id)  union semun
30  #define ARTICLE_BLOCK_PER_POOL (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT)  {
31            int val;                           /* Value for SETVAL */
32            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
33            unsigned short *array; /* Array for GETALL, SETALL */
34            struct seminfo *__buf; /* Buffer for IPC_INFO
35                                                              (Linux-specific) */
36    };
37    #endif // #if defined(_SEM_SEMUN_UNDEFINED)
38    
39    enum _section_list_constant_t
40    {
41            SECTION_TRY_LOCK_WAIT_TIME = 1, // second
42            SECTION_TRY_LOCK_TIMES = 10,
43    
44            ARTICLE_BLOCK_PER_SHM = 1000,           // sizeof(ARTICLE_BLOCK) * ARTICLE_BLOCK_PER_SHM is the size of each shm segment to allocate
45            ARTICLE_BLOCK_SHM_COUNT_LIMIT = 80, // limited by length (8-bit) of proj_id in ftok(path, proj_id)
46            ARTICLE_BLOCK_PER_POOL = (ARTICLE_BLOCK_PER_SHM * ARTICLE_BLOCK_SHM_COUNT_LIMIT),
47    
48  #define CALCULATE_PAGE_THRESHOLD 100 // Adjust to tune performance of move topic          CALCULATE_PAGE_THRESHOLD = 100, // Adjust to tune performance of moving topic between sections
49    
50            SID_STR_LEN = 5, // 32-bit + NULL
51    };
52    
53  struct article_block_t  struct article_block_t
54  {  {
55          ARTICLE articles[ARTICLE_PER_BLOCK];          ARTICLE articles[BBS_article_count_per_block];
56          int32_t article_count;          int article_count;
57          struct article_block_t *p_next_block;          struct article_block_t *p_next_block;
58  };  };
59  typedef struct article_block_t ARTICLE_BLOCK;  typedef struct article_block_t ARTICLE_BLOCK;
60    
 struct article_block_shm_t  
 {  
         int shmid;  
         void *p_shm;  
 };  
 typedef struct article_block_shm_t ARTICLE_BLOCK_SHM;  
   
61  struct article_block_pool_t  struct article_block_pool_t
62  {  {
63          ARTICLE_BLOCK_SHM shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];          int shmid;
64            struct
65            {
66                    int shmid;
67                    void *p_shm;
68            } shm_pool[ARTICLE_BLOCK_SHM_COUNT_LIMIT];
69          int shm_count;          int shm_count;
70          ARTICLE_BLOCK *p_block_free_list;          ARTICLE_BLOCK *p_block_free_list;
71          ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];          ARTICLE_BLOCK *p_block[ARTICLE_BLOCK_PER_POOL];
72          int32_t block_count;          int block_count;
73  };  };
74  typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;  typedef struct article_block_pool_t ARTICLE_BLOCK_POOL;
75    
76  static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;  static ARTICLE_BLOCK_POOL *p_article_block_pool = NULL;
77    
78  static SECTION_LIST *p_section_list_pool = NULL;  SECTION_LIST_POOL *p_section_list_pool = NULL;
 static int section_list_count = 0;  
 static TRIE_NODE *p_trie_dict_section_list = NULL;  
79    
80  int article_block_init(const char *filename, int block_count)  int article_block_init(const char *filename, int block_count)
81  {  {
# Line 82  int article_block_init(const char *filen Line 95  int article_block_init(const char *filen
95                  return -1;                  return -1;
96          }          }
97    
98          if (block_count > ARTICLE_BLOCK_PER_POOL)          if (block_count <= 0 || block_count > ARTICLE_BLOCK_PER_POOL)
99          {          {
100                  log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);                  log_error("article_block_count exceed limit %d\n", ARTICLE_BLOCK_PER_POOL);
101                  return -2;                  return -2;
102          }          }
103    
104          p_article_block_pool = calloc(1, sizeof(ARTICLE_BLOCK_POOL));          // Allocate shared memory
105          if (p_article_block_pool == NULL)          proj_id = ARTICLE_BLOCK_SHM_COUNT_LIMIT; // keep different from proj_id used to create block shm
106            key = ftok(filename, proj_id);
107            if (key == -1)
108          {          {
109                  log_error("calloc(ARTICLE_BLOCK_POOL) OOM\n");                  log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
110                  return -2;                  return -3;
111          }          }
112    
113          // Allocate shared memory          size = sizeof(ARTICLE_BLOCK_POOL);
114            shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
115            if (shmid == -1)
116            {
117                    log_error("shmget(article_block_pool_shm, size = %d) error (%d)\n", size, errno);
118                    return -3;
119            }
120            p_shm = shmat(shmid, NULL, 0);
121            if (p_shm == (void *)-1)
122            {
123                    log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
124                    return -3;
125            }
126    
127            p_article_block_pool = p_shm;
128            p_article_block_pool->shmid = shmid;
129    
130          p_article_block_pool->shm_count = 0;          p_article_block_pool->shm_count = 0;
131          pp_block_next = &(p_article_block_pool->p_block_free_list);          pp_block_next = &(p_article_block_pool->p_block_free_list);
132    
# Line 104  int article_block_init(const char *filen Line 135  int article_block_init(const char *filen
135                  block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);                  block_count_in_shm = MIN(block_count, ARTICLE_BLOCK_PER_SHM);
136                  block_count -= block_count_in_shm;                  block_count -= block_count_in_shm;
137    
138                  proj_id = getpid() + p_article_block_pool->shm_count;                  proj_id = p_article_block_pool->shm_count;
139                  key = ftok(filename, proj_id);                  key = ftok(filename, proj_id);
140                  if (key == -1)                  if (key == -1)
141                  {                  {
# Line 113  int article_block_init(const char *filen Line 144  int article_block_init(const char *filen
144                          return -3;                          return -3;
145                  }                  }
146    
147                  size = sizeof(shmid) + sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;                  size = sizeof(ARTICLE_BLOCK) * (size_t)block_count_in_shm;
148                  shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);                  shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
149                  if (shmid == -1)                  if (shmid == -1)
150                  {                  {
# Line 157  int article_block_init(const char *filen Line 188  int article_block_init(const char *filen
188    
189  void article_block_cleanup(void)  void article_block_cleanup(void)
190  {  {
191          if (p_article_block_pool != NULL)          int shmid;
192    
193            if (p_article_block_pool == NULL)
194          {          {
195                  for (int i = 0; i < p_article_block_pool->shm_count; i++)                  return;
196            }
197    
198            for (int i = 0; i < p_article_block_pool->shm_count; i++)
199            {
200                    if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
201                  {                  {
202                          if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)                          log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
203                          {                  }
                                 log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);  
                         }  
204    
205                          if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)                  if (shmctl((p_article_block_pool->shm_pool + i)->shmid, IPC_RMID, NULL) == -1)
206                          {                  {
207                                  log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);                          log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
                         }  
208                  }                  }
209            }
210    
211            shmid = p_article_block_pool->shmid;
212    
213                  free(p_article_block_pool);          if (shmdt(p_article_block_pool) == -1)
214                  p_article_block_pool = NULL;          {
215                    log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
216          }          }
217    
218            if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
219            {
220                    log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
221            }
222    
223            p_article_block_pool = NULL;
224    }
225    
226    int set_article_block_shm_readonly(void)
227    {
228            int shmid;
229            void *p_shm;
230            int i;
231    
232            if (p_article_block_pool == NULL)
233            {
234                    log_error("article_block_pool not initialized\n");
235                    return -1;
236            }
237    
238            for (i = 0; i < p_article_block_pool->shm_count; i++)
239            {
240                    shmid = (p_article_block_pool->shm_pool + i)->shmid;
241    
242                    // Remap shared memory in read-only mode
243    #if defined(__MSYS__) || defined(__MINGW32__)
244                    if (shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
245                    {
246                            log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
247                            return -2;
248                    }
249                    p_shm = shmat(shmid, (p_article_block_pool->shm_pool + i)->p_shm, SHM_RDONLY);
250    #else
251                    p_shm = shmat(shmid, (p_article_block_pool->shm_pool + i)->p_shm, SHM_RDONLY | SHM_REMAP);
252    #endif
253                    if (p_shm == (void *)-1)
254                    {
255                            log_error("shmat(article_block_pool shmid = %d) error (%d)\n", shmid, errno);
256                            return -2;
257                    }
258            }
259    
260            return 0;
261    }
262    
263    int detach_article_block_shm(void)
264    {
265            int shmid;
266    
267            if (p_article_block_pool == NULL)
268            {
269                    return -1;
270            }
271    
272            for (int i = 0; i < p_article_block_pool->shm_count; i++)
273            {
274                    if ((p_article_block_pool->shm_pool + i)->p_shm != NULL && shmdt((p_article_block_pool->shm_pool + i)->p_shm) == -1)
275                    {
276                            log_error("shmdt(shmid = %d) error (%d)\n", (p_article_block_pool->shm_pool + i)->shmid, errno);
277                            return -2;
278                    }
279            }
280    
281            shmid = p_article_block_pool->shmid;
282    
283            if (shmdt(p_article_block_pool) == -1)
284            {
285                    log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
286                    return -3;
287            }
288    
289            p_article_block_pool = NULL;
290    
291            return 0;
292  }  }
293    
294  inline static ARTICLE_BLOCK *pop_free_article_block(void)  inline static ARTICLE_BLOCK *pop_free_article_block(void)
# Line 237  ARTICLE *article_block_find_by_aid(int32 Line 351  ARTICLE *article_block_find_by_aid(int32
351          }          }
352    
353          left = 0;          left = 0;
354          right = p_article_block_pool->block_count;          right = p_article_block_pool->block_count - 1;
355    
356          // aid in the range [ head aid of blocks[left], tail aid of blocks[right - 1] ]          // aid in the range [ head aid of blocks[left], tail aid of blocks[right] ]
357          while (left < right - 1)          while (left < right)
358          {          {
359                  // get block offset no less than mid value of left and right block offsets                  // get block offset no less than mid value of left and right block offsets
360                  mid = (left + right) / 2 + (right - left) % 2;                  mid = (left + right) / 2 + (left + right) % 2;
   
                 if (mid >= p_article_block_pool->block_count)  
                 {  
                         log_error("block(mid = %d) is out of boundary\n", mid);  
                         return NULL;  
                 }  
361    
362                  if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)                  if (aid < p_article_block_pool->p_block[mid]->articles[0].aid)
363                  {                  {
364                          right = mid;                          right = mid - 1;
365                  }                  }
366                  else                  else // if (aid >= p_article_block_pool->p_block[mid]->articles[0].aid)
367                  {                  {
368                          left = mid;                          left = mid;
369                  }                  }
# Line 281  ARTICLE *article_block_find_by_aid(int32 Line 389  ARTICLE *article_block_find_by_aid(int32
389                  }                  }
390          }          }
391    
392          return (p_block->articles + left);          if (aid != p_block->articles[left].aid) // not found
393            {
394                    return NULL;
395            }
396    
397            return (p_block->articles + left); // found
398  }  }
399    
400  ARTICLE *article_block_find_by_index(int index)  ARTICLE *article_block_find_by_index(int index)
# Line 294  ARTICLE *article_block_find_by_index(int Line 407  ARTICLE *article_block_find_by_index(int
407                  return NULL;                  return NULL;
408          }          }
409    
410          if (index < 0 || index / ARTICLE_PER_BLOCK >= p_article_block_pool->block_count)          if (index < 0 || index / BBS_article_count_per_block >= p_article_block_pool->block_count)
411          {          {
412                  log_error("section_data_find_article_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);                  log_error("article_block_find_by_index(%d) is out of boundary of block [0, %d)\n", index, p_article_block_pool->block_count);
413                  return NULL;                  return NULL;
414          }          }
415    
416          p_block = p_article_block_pool->p_block[index / ARTICLE_PER_BLOCK];          p_block = p_article_block_pool->p_block[index / BBS_article_count_per_block];
417    
418          if (index % ARTICLE_PER_BLOCK >= p_block->article_count)          if (index % BBS_article_count_per_block >= p_block->article_count)
419          {          {
420                  log_error("section_data_find_article_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);                  log_error("article_block_find_by_index(%d) is out of boundary of article [0, %d)\n", index, p_block->article_count);
421                  return NULL;                  return NULL;
422          }          }
423    
424          return (p_block->articles + (index % ARTICLE_PER_BLOCK));          return (p_block->articles + (index % BBS_article_count_per_block));
425  }  }
426    
427  SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_name)  extern int section_list_init(const char *filename)
428  {  {
429          SECTION_LIST *p_section;          int semid;
430            int shmid;
431            int proj_id;
432            key_t key;
433            size_t size;
434            void *p_shm;
435            union semun arg;
436            int i;
437    
438          if (p_section_list_pool == NULL)          if (p_section_list_pool != NULL)
439          {          {
440                  p_section_list_pool = calloc(BBS_max_section, sizeof(SECTION_LIST));                  section_list_cleanup();
441                  if (p_section_list_pool == NULL)          }
442                  {  
443                          log_error("calloc(%d SECTION_LIST) OOM\n", BBS_max_section);          proj_id = (int)(time(NULL) % getpid());
444                          return NULL;          key = ftok(filename, proj_id);
445                  }          if (key == -1)
446            {
447                    log_error("ftok(%s, %d) error (%d)\n", filename, proj_id, errno);
448                    return -3;
449            }
450    
451            // Allocate shared memory
452            size = sizeof(SECTION_LIST_POOL);
453            shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0600);
454            if (shmid == -1)
455            {
456                    log_error("shmget(section_list_pool_shm, size = %d) error (%d)\n", size, errno);
457                    return -3;
458            }
459            p_shm = shmat(shmid, NULL, 0);
460            if (p_shm == (void *)-1)
461            {
462                    log_error("shmat(shmid = %d) error (%d)\n", shmid, errno);
463                    return -3;
464            }
465    
466                  section_list_count = 0;          p_section_list_pool = p_shm;
467            p_section_list_pool->shmid = shmid;
468            p_section_list_pool->section_count = 0;
469    
470            // Allocate semaphore as section locks
471            size = 2 * (BBS_max_section + 1); // r_sem and w_sem per section, the last pair for all sections
472            semid = semget(key, (int)size, IPC_CREAT | IPC_EXCL | 0600);
473            if (semid == -1)
474            {
475                    log_error("semget(section_list_pool_sem, size = %d) error (%d)\n", size, errno);
476                    return -3;
477          }          }
478    
479          if (p_trie_dict_section_list == NULL)          // Initialize sem value to 0
480            arg.val = 0;
481            for (i = 0; i < size; i++)
482          {          {
483                  p_trie_dict_section_list = trie_dict_create();                  if (semctl(semid, i, SETVAL, arg) == -1)
                 if (p_trie_dict_section_list == NULL)  
484                  {                  {
485                          log_error("trie_dict_create() OOM\n", BBS_max_section);                          log_error("semctl(section_list_pool_sem, SETVAL) error (%d)\n", errno);
486                          return NULL;                          return -3;
487                  }                  }
488          }          }
489    
490          if (section_list_count >= BBS_max_section)          p_section_list_pool->semid = semid;
491    
492            p_section_list_pool->p_trie_dict_section_by_name = trie_dict_create();
493            if (p_section_list_pool->p_trie_dict_section_by_name == NULL)
494            {
495                    log_error("trie_dict_create() OOM\n", BBS_max_section);
496                    return -2;
497            }
498    
499            p_section_list_pool->p_trie_dict_section_by_sid = trie_dict_create();
500            if (p_section_list_pool->p_trie_dict_section_by_sid == NULL)
501            {
502                    log_error("trie_dict_create() OOM\n", BBS_max_section);
503                    return -2;
504            }
505    
506            return 0;
507    }
508    
509    void section_list_cleanup(void)
510    {
511            int shmid;
512    
513            if (p_section_list_pool == NULL)
514            {
515                    return;
516            }
517    
518            if (p_section_list_pool->p_trie_dict_section_by_name != NULL)
519            {
520                    trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_name);
521                    p_section_list_pool->p_trie_dict_section_by_name = NULL;
522            }
523    
524            if (p_section_list_pool->p_trie_dict_section_by_sid != NULL)
525            {
526                    trie_dict_destroy(p_section_list_pool->p_trie_dict_section_by_sid);
527                    p_section_list_pool->p_trie_dict_section_by_sid = NULL;
528            }
529    
530            shmid = p_section_list_pool->shmid;
531    
532            if (semctl(p_section_list_pool->semid, 0, IPC_RMID) == -1)
533            {
534                    log_error("semctl(semid = %d, IPC_RMID) error (%d)\n", p_section_list_pool->semid, errno);
535            }
536    
537            if (shmdt(p_section_list_pool) == -1)
538            {
539                    log_error("shmdt(shmid = %d) error (%d)\n", shmid, errno);
540            }
541    
542            if (shmid != 0 && shmctl(shmid, IPC_RMID, NULL) == -1)
543            {
544                    log_error("shmctl(shmid = %d, IPC_RMID) error (%d)\n", shmid, errno);
545            }
546    
547            p_section_list_pool = NULL;
548    }
549    
550    int set_section_list_shm_readonly(void)
551    {
552            int shmid;
553            void *p_shm;
554    
555            if (p_section_list_pool == NULL)
556            {
557                    log_error("p_section_list_pool not initialized\n");
558                    return -1;
559            }
560    
561            shmid = p_section_list_pool->shmid;
562    
563            // Remap shared memory in read-only mode
564    #if defined(__MSYS__) || defined(__MINGW32__)
565            if (shmdt(p_section_list_pool) == -1)
566            {
567                    log_error("shmdt(section_list_pool) error (%d)\n", errno);
568                    return -1;
569            }
570            p_shm = shmat(shmid, p_section_list_pool, SHM_RDONLY);
571    #else
572            p_shm = shmat(shmid, p_section_list_pool, SHM_RDONLY | SHM_REMAP);
573    #endif
574            if (p_shm == (void *)-1)
575            {
576                    log_error("shmat(section_list_pool shmid = %d) error (%d)\n", shmid, errno);
577                    return -3;
578            }
579    
580            p_section_list_pool = p_shm;
581    
582            return 0;
583    }
584    
585    int detach_section_list_shm(void)
586    {
587            if (p_section_list_pool != NULL && shmdt(p_section_list_pool) == -1)
588          {          {
589                  log_error("section_list_count exceed limit %d\n", BBS_max_section);                  log_error("shmdt(section_list_pool) error (%d)\n", errno);
590                    return -1;
591            }
592    
593            p_section_list_pool = NULL;
594    
595            return 0;
596    }
597    
598    inline static void sid_to_str(int32_t sid, char *p_sid_str)
599    {
600            uint32_t u_sid;
601            int i;
602    
603            u_sid = (uint32_t)sid;
604            for (i = 0; i < SID_STR_LEN - 1; i++)
605            {
606                    p_sid_str[i] = (char)(u_sid % 255 + 1);
607                    u_sid /= 255;
608            }
609            p_sid_str[i] = '\0';
610    }
611    
612    SECTION_LIST *section_list_create(int32_t sid, const char *sname, const char *stitle, const char *master_list)
613    {
614            SECTION_LIST *p_section;
615            char sid_str[SID_STR_LEN];
616    
617            if (p_section_list_pool == NULL)
618            {
619                    log_error("session_list_pool not initialized\n");
620                  return NULL;                  return NULL;
621          }          }
622    
623          p_section = p_section_list_pool + section_list_count;          if (p_section_list_pool->section_count >= BBS_max_section)
624            {
625                    log_error("section_count reach limit %d >= %d\n", p_section_list_pool->section_count, BBS_max_section);
626                    return NULL;
627            }
628    
629            sid_to_str(sid, sid_str);
630    
631            p_section = p_section_list_pool->sections + p_section_list_pool->section_count;
632    
633          p_section->sid = sid;          p_section->sid = sid;
634            p_section->ex_menu_tm = 0;
635    
636          strncpy(p_section->sname, sname, sizeof(p_section->sname - 1));          strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
637          p_section->sname[sizeof(p_section->sname - 1)] = '\0';          p_section->sname[sizeof(p_section->sname) - 1] = '\0';
638    
639          strncpy(p_section->stitle, stitle, sizeof(p_section->stitle - 1));          strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
640          p_section->stitle[sizeof(p_section->stitle - 1)] = '\0';          p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
641    
642          strncpy(p_section->master_name, master_name, sizeof(p_section->master_name - 1));          strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
643          p_section->master_name[sizeof(p_section->master_name - 1)] = '\0';          p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
644    
645          if (trie_dict_set(p_trie_dict_section_list, sname, section_list_count) != 1)          if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, p_section_list_pool->section_count) != 1)
646          {          {
647                  log_error("trie_dict_set(section_data, %s, %d) error\n", sname, section_list_count);                  log_error("trie_dict_set(section, %s, %d) error\n", sname, p_section_list_pool->section_count);
648                    return NULL;
649            }
650    
651            if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, p_section_list_pool->section_count) != 1)
652            {
653                    log_error("trie_dict_set(section, %d, %d) error\n", sid, p_section_list_pool->section_count);
654                  return NULL;                  return NULL;
655          }          }
656    
657          section_list_reset_articles(p_section);          section_list_reset_articles(p_section);
658    
659          section_list_count++;          p_section_list_pool->section_count++;
660    
661          return p_section;          return p_section;
662  }  }
663    
664    int section_list_update(SECTION_LIST *p_section, const char *sname, const char *stitle, const char *master_list)
665    {
666            int64_t index;
667    
668            if (p_section == NULL || sname == NULL || stitle == NULL || master_list == NULL)
669            {
670                    log_error("NULL pointer error\n");
671                    return -1;
672            }
673    
674            index = get_section_index(p_section);
675    
676            strncpy(p_section->sname, sname, sizeof(p_section->sname) - 1);
677            p_section->sname[sizeof(p_section->sname) - 1] = '\0';
678    
679            strncpy(p_section->stitle, stitle, sizeof(p_section->stitle) - 1);
680            p_section->stitle[sizeof(p_section->stitle) - 1] = '\0';
681    
682            strncpy(p_section->master_list, master_list, sizeof(p_section->master_list) - 1);
683            p_section->master_list[sizeof(p_section->master_list) - 1] = '\0';
684    
685            if (trie_dict_set(p_section_list_pool->p_trie_dict_section_by_name, sname, index) < 0)
686            {
687                    log_error("trie_dict_set(section, %s, %d) error\n", sname, index);
688                    return -2;
689            }
690    
691            return 0;
692    }
693    
694  void section_list_reset_articles(SECTION_LIST *p_section)  void section_list_reset_articles(SECTION_LIST *p_section)
695  {  {
696          p_section->article_count = 0;          p_section->article_count = 0;
# Line 380  void section_list_reset_articles(SECTION Line 702  void section_list_reset_articles(SECTION
702    
703          p_section->page_count = 0;          p_section->page_count = 0;
704          p_section->last_page_visible_article_count = 0;          p_section->last_page_visible_article_count = 0;
705    
706            p_section->ontop_article_count = 0;
707  }  }
708    
709  void section_list_cleanup(void)  SECTION_LIST *section_list_find_by_name(const char *sname)
710  {  {
711          if (p_trie_dict_section_list != NULL)          int64_t index;
712            int ret;
713    
714            if (p_section_list_pool == NULL)
715          {          {
716                  trie_dict_destroy(p_trie_dict_section_list);                  log_error("section_list not initialized\n");
717                  p_trie_dict_section_list = NULL;                  return NULL;
718          }          }
719    
720          if (p_section_list_pool != NULL)          ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_name, sname, &index);
721            if (ret < 0)
722          {          {
723                  free(p_section_list_pool);                  log_error("trie_dict_get(section, %s) error\n", sname);
724                  p_section_list_pool = NULL;                  return NULL;
725            }
726            else if (ret == 0)
727            {
728                    return NULL;
729          }          }
730    
731          section_list_count = 0;          return (p_section_list_pool->sections + index);
732  }  }
733    
734  SECTION_LIST *section_list_find_by_name(const char *sname)  SECTION_LIST *section_list_find_by_sid(int32_t sid)
735  {  {
736          int64_t index;          int64_t index;
737            int ret;
738            char sid_str[SID_STR_LEN];
739    
740          if (p_section_list_pool == NULL || p_trie_dict_section_list == NULL)          if (p_section_list_pool == NULL)
741          {          {
742                  log_error("section_list not initialized\n");                  log_error("section_list not initialized\n");
743                  return NULL;                  return NULL;
744          }          }
745    
746          if (trie_dict_get(p_trie_dict_section_list, sname, &index) != 1)          sid_to_str(sid, sid_str);
747    
748            ret = trie_dict_get(p_section_list_pool->p_trie_dict_section_by_sid, sid_str, &index);
749            if (ret < 0)
750            {
751                    log_error("trie_dict_get(section, %d) error\n", sid);
752                    return NULL;
753            }
754            else if (ret == 0)
755          {          {
                 log_error("trie_dict_get(section_data, %s) error\n", sname);  
756                  return NULL;                  return NULL;
757          }          }
758    
759          return (p_section_list_pool + index);          return (p_section_list_pool->sections + index);
760  }  }
761    
762  int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)  int section_list_append_article(SECTION_LIST *p_section, const ARTICLE *p_article_src)
# Line 428  int section_list_append_article(SECTION_ Line 769  int section_list_append_article(SECTION_
769    
770          if (p_section == NULL || p_article_src == NULL)          if (p_section == NULL || p_article_src == NULL)
771          {          {
772                  log_error("section_list_append_article() NULL pointer error\n");                  log_error("NULL pointer error\n");
773                  return -1;                  return -1;
774          }          }
775    
# Line 438  int section_list_append_article(SECTION_ Line 779  int section_list_append_article(SECTION_
779                  return -1;                  return -1;
780          }          }
781    
782            if (p_section->sid != p_article_src->sid)
783            {
784                    log_error("section_list_append_article() error: section sid %d != article sid %d\n", p_section->sid, p_article_src->sid);
785                    return -2;
786            }
787    
788          if (p_section->article_count >= BBS_article_limit_per_section)          if (p_section->article_count >= BBS_article_limit_per_section)
789          {          {
790                  log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);                  log_error("section_list_append_article() error: article_count reach limit in section %d\n", p_section->sid);
# Line 445  int section_list_append_article(SECTION_ Line 792  int section_list_append_article(SECTION_
792          }          }
793    
794          if (p_article_block_pool->block_count == 0 ||          if (p_article_block_pool->block_count == 0 ||
795                  p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= ARTICLE_PER_BLOCK)                  p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count >= BBS_article_count_per_block)
796          {          {
797                  if ((p_block = pop_free_article_block()) == NULL)                  if ((p_block = pop_free_article_block()) == NULL)
798                  {                  {
# Line 455  int section_list_append_article(SECTION_ Line 802  int section_list_append_article(SECTION_
802    
803                  if (p_article_block_pool->block_count > 0)                  if (p_article_block_pool->block_count > 0)
804                  {                  {
805                          last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[ARTICLE_PER_BLOCK - 1].aid;                          last_aid = p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->articles[BBS_article_count_per_block - 1].aid;
806                  }                  }
807    
808                  p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;                  p_article_block_pool->p_block[p_article_block_pool->block_count] = p_block;
# Line 470  int section_list_append_article(SECTION_ Line 817  int section_list_append_article(SECTION_
817          // AID of articles should be strictly ascending          // AID of articles should be strictly ascending
818          if (p_article_src->aid <= last_aid)          if (p_article_src->aid <= last_aid)
819          {          {
820                  log_error("section_data_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);                  log_error("section_list_append_article(aid=%d) error: last_aid=%d\n", p_article_src->aid, last_aid);
821                  return -3;                  return -3;
822          }          }
823    
# Line 534  int section_list_append_article(SECTION_ Line 881  int section_list_append_article(SECTION_
881          p_section->p_article_tail = p_article;          p_section->p_article_tail = p_article;
882    
883          // Update page          // Update page
884          if ((p_article->visible && p_section->last_page_visible_article_count % BBS_article_limit_per_page == 0) ||          if ((p_article->visible && p_section->last_page_visible_article_count == BBS_article_limit_per_page) ||
885                  p_section->article_count == 1)                  p_section->page_count == 0)
886          {          {
887                  p_section->p_page_first_article[p_section->page_count] = p_article;                  p_section->p_page_first_article[p_section->page_count] = p_article;
888                  p_section->page_count++;                  p_section->page_count++;
# Line 547  int section_list_append_article(SECTION_ Line 894  int section_list_append_article(SECTION_
894                  p_section->last_page_visible_article_count++;                  p_section->last_page_visible_article_count++;
895          }          }
896    
897            if (p_article->ontop && section_list_update_article_ontop(p_section, p_article) < 0)
898            {
899                    log_error("section_list_update_article_ontop(sid=%d, aid=%d) error\n",
900                                      p_section->sid, p_article->aid);
901                    return -5;
902            }
903    
904          return 0;          return 0;
905  }  }
906    
# Line 558  int section_list_set_article_visible(SEC Line 912  int section_list_set_article_visible(SEC
912    
913          if (p_section == NULL)          if (p_section == NULL)
914          {          {
915                  log_error("section_list_set_article_visible() NULL pointer error\n");                  log_error("NULL pointer error\n");
916                  return -2;                  return -1;
917          }          }
918    
919          p_article = article_block_find_by_aid(aid);          p_article = article_block_find_by_aid(aid);
# Line 568  int section_list_set_article_visible(SEC Line 922  int section_list_set_article_visible(SEC
922                  return -1; // Not found                  return -1; // Not found
923          }          }
924    
925            if (p_section->sid != p_article->sid)
926            {
927                    log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
928                    return -2;
929            }
930    
931          if (p_article->visible == visible)          if (p_article->visible == visible)
932          {          {
933                  return 0; // Already set                  return 0; // Already set
# Line 577  int section_list_set_article_visible(SEC Line 937  int section_list_set_article_visible(SEC
937          {          {
938                  p_section->visible_article_count--;                  p_section->visible_article_count--;
939    
940                    if (user_article_cnt_inc(p_article->uid, -1) < 0)
941                    {
942                            log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_article->uid);
943                    }
944    
945                  if (p_article->tid == 0)                  if (p_article->tid == 0)
946                  {                  {
947                          p_section->visible_topic_count--;                          p_section->visible_topic_count--;
# Line 595  int section_list_set_article_visible(SEC Line 960  int section_list_set_article_visible(SEC
960                                          p_reply->visible = 0;                                          p_reply->visible = 0;
961                                          p_section->visible_article_count--;                                          p_section->visible_article_count--;
962                                          affected_count++;                                          affected_count++;
963    
964                                            if (user_article_cnt_inc(p_reply->uid, -1) < 0)
965                                            {
966                                                    log_error("user_article_cnt_inc(uid=%d, -1) error\n", p_reply->uid);
967                                            }
968                                  }                                  }
969                          }                          }
970                  }                  }
# Line 607  int section_list_set_article_visible(SEC Line 977  int section_list_set_article_visible(SEC
977                  {                  {
978                          p_section->visible_topic_count++;                          p_section->visible_topic_count++;
979                  }                  }
980    
981                    if (user_article_cnt_inc(p_article->uid, 1) < 0)
982                    {
983                            log_error("user_article_cnt_inc(uid=%d, 1) error\n", p_article->uid);
984                    }
985          }          }
986    
987          p_article->visible = visible;          p_article->visible = visible;
# Line 615  int section_list_set_article_visible(SEC Line 990  int section_list_set_article_visible(SEC
990          return affected_count;          return affected_count;
991  }  }
992    
993    int section_list_update_article_ontop(SECTION_LIST *p_section, ARTICLE *p_article)
994    {
995            int i;
996    
997            if (p_section == NULL || p_article == NULL)
998            {
999                    log_error("NULL pointer error\n");
1000                    return -1;
1001            }
1002    
1003            if (p_section->sid != p_article->sid)
1004            {
1005                    log_error("Inconsistent section sid %d != article sid %d\n", p_section->sid, p_article->sid);
1006                    return -2;
1007            }
1008    
1009            if (p_article->ontop)
1010            {
1011                    for (i = 0; i < p_section->ontop_article_count; i++)
1012                    {
1013                            if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1014                            {
1015                                    log_error("Inconsistent state found: article %d already ontop in section %d\n", p_article->aid, p_section->sid);
1016                                    return 0;
1017                            }
1018                            else if (p_section->p_ontop_articles[i]->aid > p_article->aid)
1019                            {
1020                                    break;
1021                            }
1022                    }
1023    
1024                    // Remove the oldest one if the array of ontop articles is full
1025                    if (p_section->ontop_article_count >= BBS_ontop_article_limit_per_section)
1026                    {
1027                            if (i == 0) // p_article is the oldest one
1028                            {
1029                                    return 0;
1030                            }
1031                            memmove((void *)(p_section->p_ontop_articles),
1032                                            (void *)(p_section->p_ontop_articles + 1),
1033                                            sizeof(ARTICLE *) * (size_t)(i - 1));
1034                            p_section->ontop_article_count--;
1035                            i--;
1036                    }
1037                    else
1038                    {
1039                            memmove((void *)(p_section->p_ontop_articles + i + 1),
1040                                            (void *)(p_section->p_ontop_articles + i),
1041                                            sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i));
1042                    }
1043    
1044                    p_section->p_ontop_articles[i] = p_article;
1045                    p_section->ontop_article_count++;
1046    
1047                    // TODO: debug
1048            }
1049            else // ontop == 0
1050            {
1051                    for (i = 0; i < p_section->ontop_article_count; i++)
1052                    {
1053                            if (p_section->p_ontop_articles[i]->aid == p_article->aid)
1054                            {
1055                                    break;
1056                            }
1057                    }
1058                    if (i == p_section->ontop_article_count) // not found
1059                    {
1060                            log_error("Inconsistent state found: article %d not ontop in section %d\n", p_article->aid, p_section->sid);
1061                            return 0;
1062                    }
1063    
1064                    memmove((void *)(p_section->p_ontop_articles + i),
1065                                    (void *)(p_section->p_ontop_articles + i + 1),
1066                                    sizeof(ARTICLE *) * (size_t)(p_section->ontop_article_count - i - 1));
1067                    p_section->ontop_article_count--;
1068            }
1069    
1070            return 0;
1071    }
1072    
1073    int section_list_page_count_with_ontop(SECTION_LIST *p_section)
1074    {
1075            int page_count;
1076    
1077            if (p_section == NULL)
1078            {
1079                    log_error("NULL pointer error\n");
1080                    return -1;
1081            }
1082    
1083            page_count = p_section->page_count - 1 +
1084                                     (p_section->last_page_visible_article_count + p_section->ontop_article_count + BBS_article_limit_per_page - 1) /
1085                                             BBS_article_limit_per_page;
1086    
1087            if (page_count < 0)
1088            {
1089                    page_count = 0;
1090            }
1091    
1092            return page_count;
1093    }
1094    
1095    int section_list_page_article_count_with_ontop(SECTION_LIST *p_section, int32_t page_id)
1096    {
1097            if (p_section == NULL)
1098            {
1099                    log_error("NULL pointer error\n");
1100                    return -1;
1101            }
1102    
1103            if (page_id < p_section->page_count - 1)
1104            {
1105                    return BBS_article_limit_per_page;
1106            }
1107            else // if (page_id >= p_section->page_count - 1)
1108            {
1109                    return MIN(MAX(0,
1110                                               (p_section->last_page_visible_article_count + p_section->ontop_article_count -
1111                                                    BBS_article_limit_per_page * (page_id - p_section->page_count + 1))),
1112                                       BBS_article_limit_per_page);
1113            }
1114    }
1115    
1116  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)  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)
1117  {  {
1118          ARTICLE *p_article;          ARTICLE *p_article;
# Line 628  ARTICLE *section_list_find_article_with_ Line 1126  ARTICLE *section_list_find_article_with_
1126    
1127          if (p_section == NULL)          if (p_section == NULL)
1128          {          {
1129                  log_error("section_list_find_article_with_offset() NULL pointer error\n");                  log_error("NULL pointer error\n");
1130                  return NULL;                  return NULL;
1131          }          }
1132    
# Line 640  ARTICLE *section_list_find_article_with_ Line 1138  ARTICLE *section_list_find_article_with_
1138          }          }
1139    
1140          left = 0;          left = 0;
1141          right = p_section->page_count;          right = p_section->page_count - 1;
1142    
1143          // aid in the range [ head aid of pages[left], tail aid of pages[right - 1] ]          // aid in the range [ head aid of pages[left], tail aid of pages[right] ]
1144          while (left < right - 1)          while (left < right)
1145          {          {
1146                  // get page id no less than mid value of left page id and right page id                  // get page id no less than mid value of left page id and right page id
1147                  mid = (left + right) / 2 + (right - left) % 2;                  mid = (left + right) / 2 + (left + right) % 2;
   
                 if (mid >= p_section->page_count)  
                 {  
                         log_error("page id (mid = %d) is out of boundary\n", mid);  
                         return NULL;  
                 }  
1148    
1149                  if (aid < p_section->p_page_first_article[mid]->aid)                  if (aid < p_section->p_page_first_article[mid]->aid)
1150                  {                  {
1151                          right = mid;                          right = mid - 1;
1152                  }                  }
1153                  else                  else // if (aid < p_section->p_page_first_article[mid]->aid)
1154                  {                  {
1155                          left = mid;                          left = mid;
1156                  }                  }
# Line 720  int section_list_calculate_page(SECTION_ Line 1212  int section_list_calculate_page(SECTION_
1212    
1213          if (p_section == NULL)          if (p_section == NULL)
1214          {          {
1215                  log_error("section_list_calculate_page() NULL pointer error\n");                  log_error("NULL pointer error\n");
1216                  return -1;                  return -1;
1217          }          }
1218    
# Line 734  int section_list_calculate_page(SECTION_ Line 1226  int section_list_calculate_page(SECTION_
1226    
1227          if (start_aid > 0)          if (start_aid > 0)
1228          {          {
1229                    p_article = article_block_find_by_aid(start_aid);
1230                    if (p_article == NULL)
1231                    {
1232                            return -1; // Not found
1233                    }
1234    
1235                    if (p_section->sid != p_article->sid)
1236                    {
1237                            log_error("section_list_calculate_page() error: section sid %d != start article sid %d\n", p_section->sid, p_article->sid);
1238                            return -2;
1239                    }
1240    
1241                  p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);                  p_article = section_list_find_article_with_offset(p_section, start_aid, &page, &offset, &p_next);
1242                  if (p_article == NULL)                  if (p_article == NULL)
1243                  {                  {
# Line 797  int section_list_calculate_page(SECTION_ Line 1301  int section_list_calculate_page(SECTION_
1301          } while (p_article != p_section->p_article_head);          } while (p_article != p_section->p_article_head);
1302    
1303          p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);          p_section->page_count = page + (visible_article_count > 0 ? 1 : 0);
1304          p_section->last_page_visible_article_count = visible_article_count;          p_section->last_page_visible_article_count = (visible_article_count > 0
1305                                                                                                              ? visible_article_count
1306                                                                                                              : (page > 0 ? BBS_article_limit_per_page : 0));
1307    
1308          return 0;          return 0;
1309  }  }
1310    
1311  int section_list_count_of_topic_articles(int32_t aid)  int32_t article_block_last_aid(void)
1312    {
1313            ARTICLE_BLOCK *p_block = p_article_block_pool->p_block[p_article_block_pool->block_count - 1];
1314            int32_t last_aid = p_block->articles[p_block->article_count - 1].aid;
1315    
1316            return last_aid;
1317    }
1318    
1319    int article_block_article_count(void)
1320    {
1321            int ret;
1322    
1323            if (p_article_block_pool == NULL || p_article_block_pool->block_count <= 0)
1324            {
1325                    return -1;
1326            }
1327    
1328            ret = (p_article_block_pool->block_count - 1) * BBS_article_count_per_block +
1329                      p_article_block_pool->p_block[p_article_block_pool->block_count - 1]->article_count;
1330    
1331            return ret;
1332    }
1333    
1334    int article_count_of_topic(int32_t aid)
1335  {  {
1336          ARTICLE *p_article;          ARTICLE *p_article;
1337          int article_count;          int article_count;
# Line 817  int section_list_count_of_topic_articles Line 1346  int section_list_count_of_topic_articles
1346    
1347          do          do
1348          {          {
1349                    if (p_article->tid != 0 && p_article->tid != aid)
1350                    {
1351                            log_error("article_count_of_topic(%d) error: article %d not linked to the topic\n", aid, p_article->aid);
1352                            break;
1353                    }
1354    
1355                  article_count++;                  article_count++;
1356                  p_article = p_article->p_topic_next;                  p_article = p_article->p_topic_next;
1357          } while (p_article->aid != aid);          } while (p_article->aid != aid);
# Line 836  int section_list_move_topic(SECTION_LIST Line 1371  int section_list_move_topic(SECTION_LIST
1371          int32_t first_inserted_aid_dest;          int32_t first_inserted_aid_dest;
1372          int move_counter;          int move_counter;
1373    
1374          if (p_section_dest == NULL)          if (p_section_src == NULL || p_section_dest == NULL)
1375          {          {
1376                  log_error("section_list_move_topic() NULL pointer error\n");                  log_error("NULL pointer error\n");
1377                  return -1;                  return -1;
1378          }          }
1379    
1380          if ((p_article = section_list_find_article_with_offset(p_section_src, aid, &page, &offset, &p_next)) == NULL)          if (p_section_src->sid == p_section_dest->sid)
1381          {          {
1382                  log_error("section_list_move_topic() error: article %d not found in section %d\n", aid, p_section_src->sid);                  log_error("section_list_move_topic() src and dest section are the same\n");
1383                    return -1;
1384            }
1385    
1386            if ((p_article = article_block_find_by_aid(aid)) == NULL)
1387            {
1388                    log_error("article_block_find_by_aid(aid = %d) error: article not found\n", aid);
1389                    return -2;
1390            }
1391    
1392            if (p_section_src->sid != p_article->sid)
1393            {
1394                    log_error("section_list_move_topic() error: src section sid %d != article %d sid %d\n",
1395                                      p_section_src->sid, p_article->aid, p_article->sid);
1396                  return -2;                  return -2;
1397          }          }
1398    
# Line 856  int section_list_move_topic(SECTION_LIST Line 1404  int section_list_move_topic(SECTION_LIST
1404    
1405          last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);          last_unaffected_aid_src = (p_article == p_section_src->p_article_head ? 0 : p_article->p_prior->aid);
1406    
1407          move_article_count = section_list_count_of_topic_articles(aid);          move_article_count = article_count_of_topic(aid);
1408          if (move_article_count <= 0)          if (move_article_count <= 0)
1409          {          {
1410                  log_error("section_list_count_of_topic_articles(aid = %d) <= 0\n", aid);                  log_error("article_count_of_topic(aid = %d) <= 0\n", aid);
1411                  return -2;                  return -2;
1412          }          }
1413    
# Line 876  int section_list_move_topic(SECTION_LIST Line 1424  int section_list_move_topic(SECTION_LIST
1424    
1425          do          do
1426          {          {
1427                  if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)                  if (p_section_src->sid != p_article->sid)
1428                  {                  {
1429                          log_error("section_list_move_topic() error: article %d already in section %d\n", p_article->aid, p_section_dest->sid);                          log_error("section_list_move_topic() warning: src section sid %d != article %d sid %d\n",
1430                          return -4;                                            p_section_src->sid, p_article->aid, p_article->sid);
1431                            p_article = p_article->p_topic_next;
1432                            continue;
1433                  }                  }
1434    
1435                  // Remove from bi-directional article list of src section                  // Remove from bi-directional article list of src section
# Line 900  int section_list_move_topic(SECTION_LIST Line 1450  int section_list_move_topic(SECTION_LIST
1450                  p_article->p_prior->p_next = p_article->p_next;                  p_article->p_prior->p_next = p_article->p_next;
1451                  p_article->p_next->p_prior = p_article->p_prior;                  p_article->p_next->p_prior = p_article->p_prior;
1452    
1453                    // Update sid of article
1454                    p_article->sid = p_section_dest->sid;
1455    
1456                    if (section_list_find_article_with_offset(p_section_dest, p_article->aid, &page, &offset, &p_next) != NULL)
1457                    {
1458                            log_error("section_list_move_topic() warning: article %d already in section %d\n", p_article->aid, p_section_dest->sid);
1459                            p_article = p_article->p_topic_next;
1460                            continue;
1461                    }
1462    
1463                  // Insert into bi-directional article list of dest section                  // Insert into bi-directional article list of dest section
1464                  if (p_next == NULL) // empty section                  if (p_next == NULL) // empty section
1465                  {                  {
# Line 965  int section_list_move_topic(SECTION_LIST Line 1525  int section_list_move_topic(SECTION_LIST
1525                          // Re-calculate pages of desc section                          // Re-calculate pages of desc section
1526                          if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)                          if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1527                          {                          {
1528                                  log_error("section_list_calculate_page(section = %d, aid = %d) error\n",                                  log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1529                                                    p_section_dest->sid, first_inserted_aid_dest);                                                    p_section_dest->sid, first_inserted_aid_dest);
1530                          }                          }
1531    
# Line 975  int section_list_move_topic(SECTION_LIST Line 1535  int section_list_move_topic(SECTION_LIST
1535    
1536          if (p_section_dest->article_count - dest_article_count_old != move_article_count)          if (p_section_dest->article_count - dest_article_count_old != move_article_count)
1537          {          {
1538                  log_error("section_list_move_topic() error: count of moved articles %d != %d\n",                  log_error("section_list_move_topic() warning: count of moved articles %d != %d\n",
1539                                    p_section_dest->article_count - dest_article_count_old, move_article_count);                                    p_section_dest->article_count - dest_article_count_old, move_article_count);
1540          }          }
1541    
1542          // Re-calculate pages of src section          // Re-calculate pages of src section
1543          if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)          if (section_list_calculate_page(p_section_src, last_unaffected_aid_src) < 0)
1544          {          {
1545                  log_error("section_list_calculate_page(section = %d, aid = %d) error at aid = %d\n",                  log_error("section_list_calculate_page(src section = %d, aid = %d) error at aid = %d\n",
1546                                    p_section_src->sid, last_unaffected_aid_src, aid);                                    p_section_src->sid, last_unaffected_aid_src, aid);
1547          }          }
1548    
# Line 991  int section_list_move_topic(SECTION_LIST Line 1551  int section_list_move_topic(SECTION_LIST
1551                  // Re-calculate pages of desc section                  // Re-calculate pages of desc section
1552                  if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)                  if (section_list_calculate_page(p_section_dest, first_inserted_aid_dest) < 0)
1553                  {                  {
1554                          log_error("section_list_calculate_page(section = %d, aid = %d) error\n",                          log_error("section_list_calculate_page(dest section = %d, aid = %d) error\n",
1555                                            p_section_dest->sid, first_inserted_aid_dest);                                            p_section_dest->sid, first_inserted_aid_dest);
1556                  }                  }
1557          }          }
1558    
1559          return move_article_count;          return move_article_count;
1560  }  }
1561    
1562    int get_section_index(SECTION_LIST *p_section)
1563    {
1564            int index;
1565    
1566            if (p_section_list_pool == NULL)
1567            {
1568                    log_error("get_section_index() error: uninitialized\n");
1569                    return -1;
1570            }
1571    
1572            if (p_section == NULL)
1573            {
1574                    index = BBS_max_section;
1575            }
1576            else
1577            {
1578                    index = (int)(p_section - p_section_list_pool->sections);
1579                    if (index < 0 || index >= BBS_max_section)
1580                    {
1581                            log_error("get_section_index(%d) error: index out of range\n", index);
1582                            return -2;
1583                    }
1584            }
1585    
1586            return index;
1587    }
1588    
1589    int get_section_info(SECTION_LIST *p_section, char *sname, char *stitle, char *master_list)
1590    {
1591            if (p_section == NULL)
1592            {
1593                    log_error("NULL pointer error\n");
1594                    return -1;
1595            }
1596    
1597            if (section_list_rd_lock(p_section) < 0)
1598            {
1599                    log_error("section_list_rd_lock(sid=%d) error\n", p_section->sid);
1600                    return -2;
1601            }
1602    
1603            if (sname != NULL)
1604            {
1605                    memcpy(sname, p_section->sname, sizeof(p_section->sname));
1606            }
1607            if (stitle != NULL)
1608            {
1609                    memcpy(stitle, p_section->stitle, sizeof(p_section->stitle));
1610            }
1611            if (master_list != NULL)
1612            {
1613                    memcpy(master_list, p_section->master_list, sizeof(p_section->master_list));
1614            }
1615    
1616            // release lock of section
1617            if (section_list_rd_unlock(p_section) < 0)
1618            {
1619                    log_error("section_list_rd_unlock(sid=%d) error\n", p_section->sid);
1620                    return -2;
1621            }
1622    
1623            return 0;
1624    }
1625    
1626    int section_list_try_rd_lock(SECTION_LIST *p_section, int wait_sec)
1627    {
1628            int index;
1629            struct sembuf sops[4];
1630    #if !defined(__MSYS__) && !defined(__MINGW32__)
1631            struct timespec timeout;
1632    #endif
1633            int ret;
1634    
1635            index = get_section_index(p_section);
1636            if (index < 0)
1637            {
1638                    return -2;
1639            }
1640    
1641            sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1642            sops[0].sem_op = 0;                                                                // wait until unlocked
1643            sops[0].sem_flg = 0;
1644    
1645            sops[1].sem_num = (unsigned short)(index * 2); // r_sem of section index
1646            sops[1].sem_op = 1;                                                        // lock
1647            sops[1].sem_flg = SEM_UNDO;                                        // undo on terminate
1648    
1649            // Read lock on any specific section will also acquire single read lock on "all section"
1650            // so that write lock on all section only need to acquire single write on on "all section"
1651            // rather than to acquire multiple write locks on all the available sections.
1652            if (index != BBS_max_section)
1653            {
1654                    sops[2].sem_num = BBS_max_section * 2 + 1; // w_sem of all section
1655                    sops[2].sem_op = 0;                                                // wait until unlocked
1656                    sops[2].sem_flg = 0;
1657    
1658                    sops[3].sem_num = BBS_max_section * 2; // r_sem of all section
1659                    sops[3].sem_op = 1;                                        // lock
1660                    sops[3].sem_flg = SEM_UNDO;                        // undo on terminate
1661            }
1662    
1663    #if defined(__MSYS__) || defined(__MINGW32__)
1664            ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4));
1665    #else
1666            timeout.tv_sec = wait_sec;
1667            timeout.tv_nsec = 0;
1668    
1669            ret = semtimedop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 2 : 4), &timeout);
1670    #endif
1671            if (ret == -1 && errno != EAGAIN && errno != EINTR)
1672            {
1673                    log_error("semop(index = %d, lock read) error %d\n", index, errno);
1674            }
1675    
1676            return ret;
1677    }
1678    
1679    int section_list_try_rw_lock(SECTION_LIST *p_section, int wait_sec)
1680    {
1681            int index;
1682            struct sembuf sops[3];
1683    #if !defined(__MSYS__) && !defined(__MINGW32__)
1684            struct timespec timeout;
1685    #endif
1686            int ret;
1687    
1688            index = get_section_index(p_section);
1689            if (index < 0)
1690            {
1691                    return -2;
1692            }
1693    
1694            sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1695            sops[0].sem_op = 0;                                                                // wait until unlocked
1696            sops[0].sem_flg = 0;
1697    
1698            sops[1].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1699            sops[1].sem_op = 1;                                                                // lock
1700            sops[1].sem_flg = SEM_UNDO;                                                // undo on terminate
1701    
1702            sops[2].sem_num = (unsigned short)(index * 2); // r_sem of section index
1703            sops[2].sem_op = 0;                                                        // wait until unlocked
1704            sops[2].sem_flg = 0;
1705    
1706    #if defined(__MSYS__) || defined(__MINGW32__)
1707            ret = semop(p_section_list_pool->semid, sops, 3);
1708    #else
1709            timeout.tv_sec = wait_sec;
1710            timeout.tv_nsec = 0;
1711    
1712            ret = semtimedop(p_section_list_pool->semid, sops, 3, &timeout);
1713    #endif
1714            if (ret == -1 && errno != EAGAIN && errno != EINTR)
1715            {
1716                    log_error("semop(index = %d, lock write) error %d\n", index, errno);
1717            }
1718    
1719            return ret;
1720    }
1721    
1722    int section_list_rd_unlock(SECTION_LIST *p_section)
1723    {
1724            int index;
1725            struct sembuf sops[2];
1726            int ret;
1727    
1728            index = get_section_index(p_section);
1729            if (index < 0)
1730            {
1731                    return -2;
1732            }
1733    
1734            sops[0].sem_num = (unsigned short)(index * 2); // r_sem of section index
1735            sops[0].sem_op = -1;                                               // unlock
1736            sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO;           // no wait
1737    
1738            if (index != BBS_max_section)
1739            {
1740                    sops[1].sem_num = BBS_max_section * 2;   // r_sem of all section
1741                    sops[1].sem_op = -1;                                     // unlock
1742                    sops[1].sem_flg = IPC_NOWAIT | SEM_UNDO; // no wait
1743            }
1744    
1745            ret = semop(p_section_list_pool->semid, sops, (index == BBS_max_section ? 1 : 2));
1746            if (ret == -1 && errno != EAGAIN && errno != EINTR)
1747            {
1748                    log_error("semop(index = %d, unlock read) error %d\n", index, errno);
1749            }
1750    
1751            return ret;
1752    }
1753    
1754    int section_list_rw_unlock(SECTION_LIST *p_section)
1755    {
1756            int index;
1757            struct sembuf sops[1];
1758            int ret;
1759    
1760            index = get_section_index(p_section);
1761            if (index < 0)
1762            {
1763                    return -2;
1764            }
1765    
1766            sops[0].sem_num = (unsigned short)(index * 2 + 1); // w_sem of section index
1767            sops[0].sem_op = -1;                                                       // unlock
1768            sops[0].sem_flg = IPC_NOWAIT | SEM_UNDO;                   // no wait
1769    
1770            ret = semop(p_section_list_pool->semid, sops, 1);
1771            if (ret == -1 && errno != EAGAIN && errno != EINTR)
1772            {
1773                    log_error("semop(index = %d, unlock write) error %d\n", index, errno);
1774            }
1775    
1776            return ret;
1777    }
1778    
1779    int section_list_rd_lock(SECTION_LIST *p_section)
1780    {
1781            int timer = 0;
1782            int sid = (p_section == NULL ? 0 : p_section->sid);
1783            int ret = -1;
1784    
1785            while (!SYS_server_exit)
1786            {
1787                    ret = section_list_try_rd_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1788                    if (ret == 0) // success
1789                    {
1790                            break;
1791                    }
1792                    else if (errno == EAGAIN || errno == EINTR) // retry
1793                    {
1794                            timer++;
1795                            if (timer % SECTION_TRY_LOCK_TIMES == 0)
1796                            {
1797                                    log_error("section_list_try_rd_lock() tried %d times on section %d\n", timer, sid);
1798                            }
1799                    }
1800                    else // failed
1801                    {
1802                            log_error("section_list_try_rd_lock() failed on section %d\n", sid);
1803                            break;
1804                    }
1805            }
1806    
1807            return ret;
1808    }
1809    
1810    int section_list_rw_lock(SECTION_LIST *p_section)
1811    {
1812            int timer = 0;
1813            int sid = (p_section == NULL ? 0 : p_section->sid);
1814            int ret = -1;
1815    
1816            while (!SYS_server_exit)
1817            {
1818                    ret = section_list_try_rw_lock(p_section, SECTION_TRY_LOCK_WAIT_TIME);
1819                    if (ret == 0) // success
1820                    {
1821                            break;
1822                    }
1823                    else if (errno == EAGAIN || errno == EINTR) // retry
1824                    {
1825                            timer++;
1826                            if (timer % SECTION_TRY_LOCK_TIMES == 0)
1827                            {
1828                                    log_error("section_list_try_rw_lock() tried %d times on section %d\n", timer, sid);
1829                            }
1830                    }
1831                    else // failed
1832                    {
1833                            log_error("section_list_try_rw_lock() failed on section %d\n", sid);
1834                            break;
1835                    }
1836            }
1837    
1838            return ret;
1839    }


Legend:
Removed lines/characters  
Changed lines/characters
  Added lines/characters

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