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


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

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