Skip to content
Snippets Groups Projects
Commit 03937004 authored by Per Cederqvist's avatar Per Cederqvist
Browse files

(copy_public_confs): Handle new range-based representation of memberships.

(adjust_read): Ditto.
(insert_loc_no): Ditto.
(do_add_member): Ditto.
(do_sub_member): Ditto.
(check_membership): Ditto.
(mark_as_read): Ditto.
(do_get_membership): Ditto.
(get_unread_confs): Ditto.
(set_unread): Ditto.
(set_last_read): Ditto.
(last_text_read): New static inline function.
parent d3089e2f
No related branches found
No related tags found
No related merge requests found
/*
* $Id: membership.c,v 0.72 2002/08/10 17:03:27 ceder Exp $
* $Id: membership.c,v 0.73 2002/11/06 18:40:28 ceder Exp $
* Copyright (C) 1991-2002 Lysator Academic Computer Association.
*
* This file is part of the LysKOM server.
......@@ -41,6 +41,7 @@
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#include <stddef.h>
#include <time.h>
#include <setjmp.h>
#include <sys/types.h>
......@@ -168,14 +169,13 @@ copy_public_confs (Connection * conn, /* The connection for which we copy */
*censor_m = *orig_m;
if (!want_read)
censor_m->read_texts = NULL;
censor_m->skip_read_texts = TRUE;
if (vis == mv_censor_unread)
{
censor_m->last_time_read = NO_TIME;
censor_m->last_text_read = 0;
censor_m->no_of_read = 0;
censor_m->read_texts = NULL;
censor_m->no_of_read_ranges = 0;
censor_m->read_ranges = NULL;
}
++censor_m;
......@@ -371,26 +371,30 @@ add_rec_time(Conference * conf_c,
}
/*
* Check if there are some texts immediately following last_text_read
* that are read or deleted. If so, update last_text_read and delete them
* from read_texts.
* Check if any read_ranges can be extended due to deleted texts.
* Merge read_ranges blocks, if possible.
*
* This is only used from mark_as_read().
* This is currently only used from mark_as_read(), but it would be
* good to have a background task that does this whenever a text has
* been deleted.
*/
static void
adjust_read(Membership *m,
const Conference *conf)
{
/* The first HANDLED texts in read_texts in M are already
included in last_text_read. */
unsigned short handled;
unsigned short i;
Local_text_no * locp;
struct read_range *ptr;
struct read_range *prev;
struct read_range *next;
struct read_range *tail;
struct read_range *begin;
struct read_range *end;
Local_text_no conf_max; /* Highest used local_text_no in conf */
Local_text_no conf_min; /* Lowest used local_text_no in conf */
#ifndef NDEFENSIVE_CHECKS
Local_text_no prev;
#endif
/* If nothing is marked as read, there is nothing to adjust. */
if (m->no_of_read_ranges == 0)
return;
/* (conf_min <= x < conf_max) if x is an existing local_text_no */
conf_min = l2g_next_key(&conf->texts, 0);
......@@ -398,82 +402,126 @@ adjust_read( Membership * m,
conf_min = l2g_first_appendable_key(&conf->texts);
conf_max = l2g_first_appendable_key(&conf->texts);
/* Flag all removed texts as read, if that is not already done. */
if (m->last_text_read < conf_min - 1)
m->last_text_read = conf_min - 1;
begin = &m->read_ranges[0];
end = begin + m->no_of_read_ranges;
/* Skip any texts in read_texts which are already handled. */
for (handled = 0; handled < m->no_of_read
&& m->read_texts[handled] < m->last_text_read + 1; )
{
handled++;
}
#ifdef DEBUG_MARK_AS_READ
/* Check that the items in read_texts really ARE sorted in ascending order.
If not, there is probably a bug in this routine or in mark_as_read. */
/* This loops advances m->last_text_read as far as possible,
advancing handled along when appropriate. m->last_text_read
can be increased for one of two reasons:
- The next text is present in read_texts.
- The next text is deleted.
This loop handles both cases. */
for ( ; ; )
for (ptr = begin; ptr < end; ptr++)
{
if (handled < m->no_of_read
&& m->read_texts[handled] == m->last_text_read + 1)
if (ptr->first_read > ptr->last_read)
{
/* This text is present in read_texts. */
m->last_text_read++;
handled++;
kom_log("Bad input to adjust_read. Conference %lu, Priority %lu."
" Range %lu-%lu is nonsensical.\n",
(unsigned long)m->conf_no,
(unsigned long)m->priority,
(unsigned long)ptr->first_read,
(unsigned long)ptr->last_read);
return;
}
else if (m->last_text_read + 1 < conf_max
&& l2g_lookup(&conf->texts, m->last_text_read + 1) == 0)
if (ptr != begin && (ptr-1)->last_read >= ptr->first_read)
{
/* This text is deleted. */
m->last_text_read++;
kom_log("Bad input to adjust_read. Conference %lu, Priority %lu."
" Overlapping ranges: %lu-%lu, %lu-%lu.\n",
(unsigned long)m->conf_no,
(unsigned long)m->priority,
(unsigned long)(ptr-1)->first_read,
(unsigned long)(ptr-1)->last_read,
(unsigned long)ptr->first_read,
(unsigned long)ptr->last_read);
return;
}
}
#endif
for (ptr = begin; ptr < end; ptr++)
{
if (ptr == begin)
prev = NULL;
else
break;
prev = ptr - 1;
next = ptr + 1;
if (next == end)
next = NULL;
/* Try to lower first_read. */
while (ptr->first_read > conf_min
&& (prev == NULL || prev->last_read + 1 < ptr->first_read)
&& l2g_lookup(&conf->texts, ptr->first_read - 1) == 0)
{
ptr->first_read--;
}
/* Delete all handled entries in read_texts. */
if (handled > 0)
/* Try to raise last_read. */
while (ptr->last_read < conf_max - 1
&& (next == NULL || ptr->last_read < next->first_read - 1)
&& l2g_lookup(&conf->texts, ptr->last_read + 1) == 0)
{
m->no_of_read -= handled;
ptr->last_read++;
}
}
for (locp = m->read_texts;
locp < m->read_texts + m->no_of_read;
locp++)
/* Join any ranges that became adjacent. */
tail = begin;
for (ptr = begin + 1; ptr < end; ptr++)
{
*locp = *(locp + handled);
if (tail->last_read + 1 == ptr->first_read)
tail->last_read = ptr->last_read;
else if (++tail != ptr)
*tail = *ptr;
}
if (end != tail + 1)
{
end = tail + 1;
m->no_of_read_ranges = end - begin;
m->read_ranges = srealloc(
m->read_ranges,
m->no_of_read_ranges * sizeof(m->read_ranges[0]));
}
#ifndef NDEFENSIVE_CHECKS
/* Check that the items in read_texts really ARE sorted in ascending order.
If not, there is probably a bug in this routine or in mark_as_read */
prev = m->last_text_read;
If not, there is probably a bug in this routine or in mark_as_read. */
for ( i = 0; i < m->no_of_read; i++)
for (ptr = begin; ptr < end; ptr++)
{
if ( prev >= m->read_texts[ i ] )
if (ptr->first_read > ptr->last_read)
{
kom_log("Bug in adjust_read. Conference %lu, Priority %lu\n"
"\tprev = %lu, i = %lu, m->read_texts[i] = %lu\n",
(unsigned long)m->conf_no, (unsigned long)m->priority,
(unsigned long)prev, (unsigned long)i,
(unsigned long)m->read_texts[i]);
kom_log("Bug in adjust_read. Conference %lu, Priority %lu."
" Range %lu-%lu is nonsensical (fixed).\n",
(unsigned long)m->conf_no,
(unsigned long)m->priority,
(unsigned long)ptr->first_read,
(unsigned long)ptr->last_read);
ptr->last_read = ptr->first_read;
return;
}
prev = m->read_texts[ i ];
if (ptr != begin && ptr->first_read <= (ptr-1)->last_read)
{
kom_log("Bug in adjust_read. Conference %lu, Priority %lu."
" %lu not greater than %lu (fixed).\n",
(unsigned long)m->conf_no,
(unsigned long)m->priority,
(unsigned long)ptr->first_read,
(unsigned long)(ptr-1)->last_read);
sfree(m->read_ranges);
m->read_ranges = NULL;
m->no_of_read_ranges = 0;
return;
}
}
#endif
}
/*
* insert TEXT in the list of read_texts in M. The texts are sorted.
* m->no_of_read is updated. m->read_texts is never reallocated, and must
* thus be big enough to hold the new number.
* insert TEXT in the list of read texts in M. The texts are sorted.
* m->no_of_read is updated.
*
* Returns FAILURE if the text is already read.
*
......@@ -484,38 +532,74 @@ static Success
insert_loc_no(Local_text_no text,
Membership * m)
{
Local_text_no * seek, * move;
struct read_range *lo;
struct read_range *hi;
struct read_range *mid;
struct read_range *begin;
struct read_range *end;
ptrdiff_t save;
struct read_range *gap;
struct read_range *move;
if (m->no_of_read_ranges == 0)
{
m->read_ranges = smalloc(sizeof(m->read_ranges[0]));
m->no_of_read_ranges = 1;
m->read_ranges[0].first_read = text;
m->read_ranges[0].last_read = text;
return OK;
}
begin = lo = &m->read_ranges[0];
end = hi = lo + m->no_of_read_ranges;
if ( text <= m->last_text_read )
while (hi - lo > 1)
{
return FAILURE; /* This text was already read. */
mid = lo + (hi - lo) / 2;
if (text < mid->first_read - 1)
hi = mid;
else if (text > mid->last_read + 1)
lo = mid;
else
{
lo = mid;
hi = mid + 1;
}
}
for ( seek = m->read_texts; seek < m->read_texts + m->no_of_read; seek++)
{
if ( text == *seek )
{
if (text >= lo->first_read && text <= lo->last_read)
return FAILURE; /* This text was already read. */
}
if ( text < *seek )
{ /* The text should be entered here. */
for ( move = m->read_texts + m->no_of_read; move > seek; move--)
if (text == lo->first_read - 1)
{
*move = *(move - 1);
if (lo != begin && (lo-1)->last_read == text)
return FAILURE; /* This text was already read. */
lo->first_read = text;
return OK;
}
*seek = text;
++(m->no_of_read);
if (text == lo->last_read + 1)
{
if (lo != end && (lo+1)->first_read == text)
return FAILURE; /* This text was already read. */
lo->last_read = text;
return OK;
}
}
*seek = text; /* The text had a higher number than any */
++(m->no_of_read); /* previously read text. */
/* We have to allocate a new block. */
if (lo->last_read < text)
lo++;
save = lo - begin;
m->read_ranges = srealloc(
m->read_ranges,
++(m->no_of_read_ranges) * sizeof(m->read_ranges[0]));
begin = &m->read_ranges[0];
gap = begin + save;
end = begin + m->no_of_read_ranges;
for (move = end - 1; move > gap; move--)
*move = *(move - 1);
gap->first_read = gap->last_read = text;
return OK;
}
......@@ -541,6 +625,19 @@ send_async_new_membership(Pers_no pers_no,
}
}
/*
* Compute the derived last-text-read value.
*/
static inline Local_text_no
last_text_read(const Membership *mship)
{
if (mship->no_of_read_ranges == 0)
return 0;
if (mship->read_ranges[0].first_read > 1)
return 0;
return mship->read_ranges[0].last_read;
}
/*
* End of static functions
......@@ -606,10 +703,10 @@ do_add_member(Conf_no conf_no, /* Conference to add a new member to. */
mship->conf_no = conf_no;
mship->priority = priority;
mship->last_time_read = current_time;
mship->last_text_read = 0;
mship->no_of_read = 0;
mship->read_texts = NULL;
mship->no_of_read_ranges = 0;
mship->read_ranges = NULL;
mship->type = *type;
mship->skip_read_texts = FALSE;
/* Make room for the person in the conference */
......@@ -694,7 +791,7 @@ do_sub_member(Conf_no conf_no, /* Conf to delete member from. */
/* Delete from Person */
sfree( mship->read_texts );
sfree(mship->read_ranges);
--pers_p->conferences.no_of_confs;
while ( mship
< pers_p->conferences.confs + pers_p->conferences.no_of_confs )
......@@ -1116,39 +1213,79 @@ add_member(Conf_no conf_no,
static int
check_membership(Pers_no pno,
const Conference *conf,
const Membership *mship)
Membership *mship)
{
static int log_no = 0;
int errors = 0;
int i;
Local_text_no last=0;
int log_no=0;
struct read_range *begin;
struct read_range *end;
struct read_range *ptr;
if (mship->no_of_read_ranges == 0 && mship->read_ranges != NULL)
{
if (log_no < 80)
kom_log("ERROR: check_membership(): (%d): "
"no_of_read_ranges == 0 but read_ranges != NULL\n",
++log_no);
/* We don't know what it points to, so we dare not free it. */
mship->read_ranges = NULL;
errors++;
}
/* Check read texts */
if (mship->last_text_read >= l2g_first_appendable_key(&conf->texts))
if (mship->no_of_read_ranges != 0 && mship->read_ranges == NULL)
{
if ( log_no++ < 80 )
kom_log("%s%d) Person %lu has read text %lu in conf %lu%s%lu texts.\n",
"membership.c: check_membership(): (",
log_no,
(unsigned long)pno,
(unsigned long)mship->last_text_read,
(unsigned long)mship->conf_no,
", which only has ",
(unsigned long)(l2g_first_appendable_key(&conf->texts)
- 1));
if (log_no < 80)
kom_log("ERROR: check_membership(): (%d): "
"no_of_read_ranges == %ld but read_ranges == NULL\n",
++log_no, (long)mship->no_of_read_ranges);
mship->no_of_read_ranges = 0;
errors++;
}
last = mship->last_text_read;
if (mship->read_ranges == NULL)
return errors;
begin = &mship->read_ranges[0];
end = begin + mship->no_of_read_ranges;
for ( i = 0; i < mship->no_of_read; i++)
for (ptr = begin; ptr < end; ptr++)
{
if (ptr != begin && (ptr-1)->last_read + 1 >= ptr->first_read)
{
if ( mship->read_texts[i] <= last )
if (log_no < 80)
kom_log("ERROR: check_membership(): (%d): "
"bad range sequence %lu-%lu, %lu-%lu\n",
++log_no,
(unsigned long)(ptr-1)->first_read,
(unsigned long)(ptr-1)->last_read,
(unsigned long)ptr->first_read,
(unsigned long)ptr->last_read);
errors++;
}
if (ptr->first_read > ptr->last_read)
{
if (log_no < 80)
kom_log("ERROR: check_membership(): (%d): bad range %lu-%lu\n",
++log_no,
(unsigned long)ptr->first_read,
(unsigned long)ptr->last_read);
errors++;
}
}
last = mship->read_texts[i];
if ((end-1)->last_read >= l2g_first_appendable_key(&conf->texts))
{
if (log_no < 80)
kom_log("ERROR: check_membership(): (%d): Person %lu has read text"
" %lu in conf %lu, which only has %lu texts.\n",
log_no,
(unsigned long)pno,
(unsigned long)(end-1)->last_read,
(unsigned long)mship->conf_no,
(unsigned long)(l2g_first_appendable_key(&conf->texts)-1));
errors++;
}
return errors;
......@@ -1159,7 +1296,7 @@ check_membership(Pers_no pno,
* mark_as_read() is used to tell LysKOM which texts you have read.
* You can mark several texts in one chunk, but the chunk should not
* be too big to prevent users from having to re-read texts in case of
* a [server/client/network]-crash.
* a server/client/network crash.
*
* The texts are marked per conference. If there are several recipients
* to a text it should be mark_as_read() in all the recipients.
......@@ -1189,11 +1326,10 @@ mark_as_read (Conf_no conference,
{
int i;
Membership * m;
int allocflg = 0; /* read_texts is not re-allocated yet */
Conference * conf_c;
Success retval = OK;
#ifdef DEBUG_MARK_AS_READ
const Local_text_no * const text_arr_start = text_arr;
#ifdef DEBUG_MARK_AS_READ
Membership original;
int loop;
static int log_no = 0;
......@@ -1212,18 +1348,19 @@ mark_as_read (Conf_no conference,
}
#ifdef DEBUG_MARK_AS_READ
if ( m->read_texts == NULL && m->no_of_read != 0 )
if ( m->read_ranges == NULL && m->no_of_read_ranges != 0 )
{
kom_log("mark_as_read(): m->read_texts == NULL && "
"m->no_of_read == %lu (corrected).\n",
(unsigned long)m->no_of_read);
m->no_of_read = 0;
kom_log("mark_as_read(): m->read_ranges == NULL && "
"m->no_of_read_ranges == %lu (corrected).\n",
(unsigned long)m->no_of_read_ranges);
m->no_of_read_ranges = 0;
}
original = *m;
original.read_texts = smalloc(m->no_of_read * sizeof(Local_text_no));
memcpy(original.read_texts, m->read_texts,
m->no_of_read * sizeof(Local_text_no));
original.read_ranges = smalloc(m->no_of_read_ranges
* sizeof(original.read_ranges[0]));
memcpy(original.read_ranges, m->read_ranges,
m->no_of_read_ranges * sizeof(original.read_ranges[0]));
#endif
for( i = no_of_texts; i > 0; i--, text_arr++ )
......@@ -1243,7 +1380,12 @@ mark_as_read (Conf_no conference,
retval = FAILURE;
break; /* Exit for-loop */
}
}
if (retval == OK)
{
for(text_arr = text_arr_start, i = no_of_texts; i > 0; i--, text_arr++)
{
/* Is it a letter to ACTPERS? If so, add a rec_time item. */
if (conference == ACTPERS)
......@@ -1251,53 +1393,28 @@ mark_as_read (Conf_no conference,
/* Update the Membership struct */
if ( *text_arr == m->last_text_read + 1 )
{
++m->last_text_read;
if ( active_connection->cwc == conference )
++ACT_P->read_texts;
}
else
{
if ( allocflg == 0 )
{
/* Realloc as much as is needed, and probably more. */
/* Better than to execute srealloc 100 times... */
m->read_texts = srealloc( m->read_texts,
(m->no_of_read + i)
* sizeof(Local_text_no));
allocflg = 1;
}
if (insert_loc_no(*text_arr, m) == OK
&& active_connection->cwc == conference )
{
++ACT_P->read_texts;
}
}
}
adjust_read( m, conf_c ); /* Delete initial part
of read_texts in the membership. */
/* Realloc to correct size */
m->read_texts = srealloc( m->read_texts,
(m->no_of_read) * sizeof(Local_text_no));
adjust_read(m, conf_c);
mark_conference_as_changed(conference);
if (active_connection->cwc == conference)
mark_person_as_changed(ACTPERS);
}
#ifdef DEBUG_MARK_AS_READ
/* Check that the membership is correct. Otherwise log all info. */
if ( check_membership(ACTPERS, conf_c, m) > 0 && log_no++ < 40 )
if (check_membership(ACTPERS, conf_c, m) > 0 && log_no < 40)
{
kom_log("mark_as_read(): (Msg no %d) Person %lu %s:\n",
log_no, (unsigned long)ACTPERS,
"has a corrupt membership");
kom_log("Dump of data follows: <original membership> %s\n",
"<updated membership> <texts to mark>");
kom_log("mark_as_read(): (%d) Person %lu has a corrupt membership:\n",
++log_no, (unsigned long)ACTPERS);
kom_log("Dump of data follows: <original membership>"
"<updated membership> <texts to mark>\n");
foutput_membership(stderr, &original);
putc('\n', stderr);
foutput_membership(stderr, m);
......@@ -1309,7 +1426,7 @@ mark_as_read (Conf_no conference,
fprintf(stderr, "}\n");
}
sfree(original.read_texts);
sfree(original.read_ranges);
#endif
return retval;
}
......@@ -1374,7 +1491,7 @@ do_get_membership (Pers_no pers_no,
* sizeof(Membership) ));
for ( i = 0; i < temp_pers.conferences.no_of_confs; i++ )
temp_pers.conferences.confs[ i ].read_texts = NULL;
temp_pers.conferences.confs[i].skip_read_texts = TRUE;
}
......@@ -1579,7 +1696,7 @@ get_unread_confs(Pers_no pers_no,
Conf_no conf_no = confs->conf_no;
if (confs->type.passive == 0
&& (confs->last_text_read < cached_get_highest_local_no(conf_no))
&& (last_text_read(confs) < cached_get_highest_local_no(conf_no))
&& membership_visible(active_connection,
pers_no, conf_no, confs->type,
FALSE, FALSE) > mv_none)
......@@ -1617,12 +1734,20 @@ set_unread (Conf_no conf_no,
highest = l2g_first_appendable_key(&conf_c->texts) - 1;
mship->last_text_read = ((highest > no_of_unread)
? (highest - no_of_unread) : 0);
sfree(mship->read_texts);
mship->read_texts = NULL;
mship->no_of_read = 0;
if (highest > no_of_unread)
{
mship->no_of_read_ranges = 1;
mship->read_ranges = srealloc(mship->read_ranges,
1 * sizeof(mship->read_ranges[0]));
mship->read_ranges[0].first_read = 1;
mship->read_ranges[0].last_read = highest - no_of_unread;
}
else
{
sfree(mship->read_ranges);
mship->read_ranges = NULL;
mship->no_of_read_ranges = 0;
}
mark_person_as_changed(ACTPERS);
return OK;
......@@ -1654,12 +1779,23 @@ set_last_read (Conf_no conf_no,
last = l2g_first_appendable_key(&conf_c->texts) - 1;
mship->last_text_read = ((last_read <= last)
? last_read : last);
if (last_read > last)
last_read = last;
sfree(mship->read_texts);
mship->read_texts = NULL;
mship->no_of_read = 0;
if (last_read > 0)
{
mship->no_of_read_ranges = 1;
mship->read_ranges = srealloc(mship->read_ranges,
1 * sizeof(mship->read_ranges[0]));
mship->read_ranges[0].first_read = 1;
mship->read_ranges[0].last_read = last_read;
}
else
{
sfree(mship->read_ranges);
mship->read_ranges = NULL;
mship->no_of_read_ranges = 0;
}
mark_person_as_changed(ACTPERS);
return OK;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment