Пример #1
0
static void sfw_be_start (RemReader_t *rrp)
{
	ctrc_printd (RTPS_ID, RTPS_SFW_BE_START, &rrp, sizeof (rrp));
	prof_start (rtps_bw_start);

	RR_SIGNAL (rrp, "BE-Start");
	NEW_RR_CSTATE (rrp, RRCS_INITIAL, 1);
	NEW_RR_CSTATE (rrp, RRCS_READY, 0);
	NEW_RR_TSTATE (rrp, RRTS_IDLE, 1);
	rrp->rr_nack_timer = NULL;

#ifdef RTPS_MARKERS
	if (rrp->rr_writer->endpoint.mark_start)
		rtps_marker_notify (rrp->rr_writer->endpoint.endpoint, EM_START, "sfw_be_start");
#endif

	/* Add existing cache entries to reader locator/proxy queue. */
	
	hc_replay (rrp->rr_writer->endpoint.endpoint->cache,
					proxy_add_change, (uintptr_t) rrp);

	if ((rrp->rr_unsent_changes = LIST_HEAD (rrp->rr_changes)) != NULL &&
	    rrp->rr_writer->endpoint.push_mode) {
		NEW_RR_TSTATE (rrp, RRTS_PUSHING, 0);
		proxy_activate (&rrp->proxy);
	}
	prof_stop (rtps_bw_start, 1);
	CACHE_CHECK (&rrp->rr_writer->endpoint, "sfw_be_start");
}
Пример #2
0
static int sfw_be_new_change (RemReader_t      *rrp,
			      Change_t         *cp,
			      HCI              hci,
			      SequenceNumber_t *snr)
{
	CCREF	*rp;

	ARG_NOT_USED (snr)

	ctrc_printd (RTPS_ID, RTPS_SFW_BE_NEW, &rrp, sizeof (rrp));
	prof_start (rtps_bw_new);

	RR_SIGNAL (rrp, "BE-NewChange");

#ifdef RTPS_MARKERS
	if (rrp->rr_writer->endpoint.mark_newch)
		rtps_marker_notify (rrp->rr_writer->endpoint.endpoint, EM_NEW_CHANGE, "sfw_be_new_change");
#endif
	rp = change_enqueue (rrp, cp, hci, CS_UNSENT);
	if (!rp)
		return (0);

	rp->ack_req = 1;
	rrp->rr_unacked++;
	if (!rrp->rr_unsent_changes) {
		rrp->rr_unsent_changes = rp;
		proxy_activate (&rrp->proxy);
	}
	NEW_RR_TSTATE (rrp, RRTS_PUSHING, 0);
	CACHE_CHECK (&rrp->rr_writer->endpoint, "sfw_be_new_change");
	prof_stop (rtps_bw_new, 1);
	return (1);
}
Пример #3
0
static void sfw_be_rem_change (RemReader_t *rrp, Change_t *cp)
{
	ctrc_printd (RTPS_ID, RTPS_SFW_BE_REM, &rrp, sizeof (rrp));
	prof_start (rtps_bw_rem);

	RR_SIGNAL (rrp, "BE-RemChange");

#ifdef RTPS_MARKERS
	if (rrp->rr_writer->endpoint.mark_rmch)
		rtps_marker_notify (rrp->rr_writer->endpoint.endpoint, EM_REM_CHANGE, "sfw_be_rem_change");
#endif
	change_remove (rrp, cp);
	prof_stop (rtps_bw_rem, 1);
	CACHE_CHECK (&rrp->rr_writer->endpoint, "sfw_be_rem_change");
}
Пример #4
0
static void sfr_be_finish (RemWriter_t *rwp)
{
	ctrc_printd (RTPS_ID, RTPS_SFR_BE_FINISH, &rwp, sizeof (rwp));
	prof_start (rtps_br_finish);

	RW_SIGNAL (rwp, "BE-Finish");

#ifdef RTPS_MARKERS
	if (rwp->rw_reader->endpoint.mark_finish)
		rtps_marker_notify (rwp->rw_reader->endpoint.endpoint, EM_FINISH, "sfr_be_finish");
#endif

	/* We're done. */
	NEW_RW_CSTATE (rwp, RWCS_FINAL, 0);
	prof_stop (rtps_br_finish, 1);
}
Пример #5
0
static int sfw_be_send_data (RemReader_t *rrp)
{
	int	error;

	ctrc_printd (RTPS_ID, RTPS_SFW_BE_SEND, &rrp, sizeof (rrp));
	prof_start (rtps_bw_send);

	RR_SIGNAL (rrp, "BE-SendData");

#ifdef RTPS_MARKERS
	if (rrp->rr_writer->endpoint.mark_send)
		rtps_marker_notify (rrp->rr_writer->endpoint.endpoint, EM_SEND, "sfw_be_send_data");
#endif
	error = be_send_data (rrp, (DiscoveredReader_t *) &rrp->rr_endpoint);
	prof_stop (rtps_bw_send, 1);
	return (error);
}
Пример #6
0
static void sfr_be_start (RemWriter_t *rwp)
{
	ctrc_printd (RTPS_ID, RTPS_SFR_BE_START, &rwp, sizeof (rwp));
	prof_start (rtps_br_start);

	RW_SIGNAL (rwp, "BE-Start");

#ifdef RTPS_MARKERS
	if (rwp->rw_reader->endpoint.mark_start)
		rtps_marker_notify (rwp->rw_reader->endpoint.endpoint, EM_START, "sfr_be_start");
#endif
	NEW_RW_CSTATE (rwp, RWCS_INITIAL, 1);
	LIST_INIT (rwp->rr_changes);
	rwp->rw_changes.nchanges = 0;
	NEW_RW_CSTATE (rwp, RWCS_READY, 0);
	NEW_RW_ASTATE (rwp, RWAS_WAITING, 1);
	prof_stop (rtps_br_start, 1);
}
Пример #7
0
static void sfr_rel_alive_to (uintptr_t user)
{
	RemWriter_t	 *rwp = (RemWriter_t *) user;
	Reader_t	 *r = (Reader_t *) (rwp->rw_reader->endpoint.endpoint);

	ctrc_printd (RTPS_ID, RTPS_SFR_ALIVE_TO, &user, sizeof (user));
	prof_start (rtps_rr_alive_to);

#ifdef RTPS_MARKERS
	if (rwp->rw_reader->endpoint.mark_alv_to)
		rtps_marker_notify ((LocalEndpoint_t *) r, EM_ALIVE_TO, "sfr_rel_alive_to");
#endif
	if (!info_reply_rxed ()) {
		lrloc_print ("RTPS: RelAliveTO: ");
		proxy_reset_reply_locators (&rwp->proxy);	/* Reselect path. */
	}
	sfr_rel_init_acknack (rwp);
	RALIVE_TMR_START (rwp, rwp->rw_hbrsp_timer, RALIVE_TO,
			   (uintptr_t) rwp, sfr_rel_alive_to, &r->r_lock);
	prof_stop (rtps_rr_alive_to, 1);
}
Пример #8
0
static void sfr_rel_start (RemWriter_t *rwp)
{
	Reader_t	 *r = (Reader_t *) (rwp->rw_reader->endpoint.endpoint);

	ctrc_printd (RTPS_ID, RTPS_SFR_REL_START, &rwp, sizeof (rwp));
	prof_start (rtps_rr_start);

	RW_SIGNAL (rwp, "REL-Start");

#ifdef RTPS_MARKERS
	if (rwp->rw_reader->endpoint.mark_start)
		rtps_marker_notify (rwp->rw_reader->endpoint.endpoint, EM_START, "sfr_rel_start");
#endif
	NEW_RW_CSTATE (rwp, RWCS_INITIAL, 1);
	LIST_INIT (rwp->rw_changes);
	rwp->rw_changes.nchanges = 0;
	NEW_RW_CSTATE (rwp, RWCS_READY, 0);
	NEW_RW_ASTATE (rwp, RWAS_WAITING, 0);
	rwp->rw_hb_no_data = 0;
	rwp->rw_hbrsp_timer = NULL;

#ifdef RTPS_PROXY_INST
	rwp->rw_loc_inst = ++rtps_rw_insts;
#endif
#ifdef RTPS_INIT_ACKNACK

	/* Send an initial ACKNACK to bootstrap the protocol in case the writer
	   was already active, but we missed the initial HEARTBEAT! */
	rwp->rw_peer_alive = 0;
	sfr_rel_init_acknack (rwp);
	RALIVE_TMR_ALLOC (rwp);
	rwp->rw_hbrsp_timer = tmr_alloc ();
	if (rwp->rw_hbrsp_timer) {
		tmr_init (rwp->rw_hbrsp_timer, "RTPS-RAlive");
		RALIVE_TMR_START (rwp, rwp->rw_hbrsp_timer, RALIVE_TO,
				 (uintptr_t) rwp, sfr_rel_alive_to, &r->r_lock);
	}
#endif
	prof_stop (rtps_rr_start, 1);
}
Пример #9
0
static void sfw_be_finish (RemReader_t *rrp)
{
	ctrc_printd (RTPS_ID, RTPS_SFW_BE_FINISH, &rrp, sizeof (rrp));
	prof_start (rtps_bw_finish);

	RR_SIGNAL (rrp, "BE-Finish");

#ifdef RTPS_MARKERS
	if (rrp->rr_writer->endpoint.mark_finish)
		rtps_marker_notify (rrp->rr_writer->endpoint.endpoint, EM_FINISH, "sfw_be_finish");
#endif
	/* Block until reader proxy is no longer active. */
	proxy_wait_inactive (&rrp->proxy);

	/* Cleanup samples queue. */
	change_delete_enqueued (rrp);

	/* Finally we're done. */
	NEW_RR_CSTATE (rrp, RRCS_FINAL, 0);

	prof_stop (rtps_bw_finish, 1);
	CACHE_CHECK (&rrp->rr_writer->endpoint, "sfw_be_finish");
}
Пример #10
0
static void sfr_be_data (RemWriter_t         *rwp,
			 Change_t            *cp,
			 SequenceNumber_t    *cpsnr,
			 const KeyHash_t     *hp,
			 const unsigned char *key,
			 size_t              keylen,
#ifdef RTPS_FRAGMENTS
			 DataFragSMsg        *fragp,
			 FragInfo_t	     **finfo,
#endif
			 int                 ignore)
{
	Change_t	*ncp;
	READER		*rp = rwp->rw_reader;
	Reader_t	*r = (Reader_t *) rp->endpoint.endpoint;
#ifdef RTPS_FRAGMENTS
	CCREF		*refp;
	FragInfo_t	*fip;
	unsigned	max_frags = 0;
#if defined (DDS_SECURITY) && defined (DDS_NATIVE_SECURITY)
	DB		*dbp;
	DBW		walk;
	int		error;
#endif
	size_t		ofs;
#endif

	ctrc_printd (RTPS_ID, RTPS_SFR_BE_DATA, &rwp, sizeof (rwp));
	prof_start (rtps_br_data);
#ifdef RTPS_FRAGMENTS
	if (rwp->rw_changes.nchanges) {	/* Lingering fragment? */
		refp = LIST_HEAD (rwp->rw_changes);
		fip = refp->fragments;
	}
	else {
		refp = NULL;
		fip = NULL;
	}
	if (fragp) {
		STATS_INC (rwp->rw_ndatafrags);
		if (!fip) {
			max_frags = (fragp->sample_size + fragp->frag_size - 1) / fragp->frag_size;
			if (fragp->frag_start != 1 ||
			    fragp->frag_start + fragp->num_fragments - 1 > max_frags)
				return;

			ncp = hc_change_new ();
			if (!ncp) {
				warn_printf ("sfr_be_data: no memory for change!");
				return;
			}
			ncp->c_kind = cp->c_kind;
			ncp->c_writer = cp->c_writer;
			ncp->c_time = cp->c_time;
			ncp->c_seqnr = cp->c_seqnr;
			refp = ccref_add (&rwp->rw_changes, ncp, 0, 1, CS_RECEIVED);
			if (!refp) {
				warn_printf ("sfr_be_data: no memory for list element!");
				goto no_ref_mem;
			}

		    new_finfo:

			if (*finfo) {
				fip = *finfo;
				rcl_access (fip);
				fip->nrefs++;
				rcl_done (fip);
			}
			else {
				fip = *finfo = rfraginfo_create (refp, fragp, max_frags);
				if (!fip) {
					goto no_frag_mem;
				}
			}
		}
		else {
			ncp = refp->u.c.change;
			if (fip->fsize != fragp->frag_size ||
			    fip->length != fragp->sample_size ||
			    !SEQNR_EQ (ncp->c_seqnr, fragp->writer_sn)) {

				if (fragp->frag_start != 1)
					return;

				/* Incorrect fragment context: reset it. */
				if (fip->nrefs == 1) {
					fip = rfraginfo_update (refp, fragp);
					if (!fip)
						goto no_frag_mem;
				}
				else {
					rfraginfo_delete (refp);
					goto new_finfo;
				}
			}
			else if (fragp->frag_start != fip->first_na + 1 ||
				 fragp->frag_start + fragp->num_fragments - 1 >
								fip->total)
				return;
		}

		/* Update key info if present. */
		if (hp) {
			fip->hash = *hp;
			fip->hp = &fip->hash;
			fip->key = fip->hash.hash;
			fip->keylen = 12;
		}

		/* Mark the fragment as correctly received. */
		mark_fragment (fip, fragp, cp);
		fip->first_na++;

		/* Check if all fragments are received correctly. */
		if (fip->num_na) {
			FRAGSC_TMR_START (rwp,
					  &fip->timer,
					  TICKS_PER_SEC * 2,
					  (uintptr_t) rwp,
					  sfr_be_frag_to,
					  &r->r_lock);
			return;
		}

#if defined (DDS_SECURITY) && defined (DDS_NATIVE_SECURITY)

		/* Decrypt payload data if an encrypted payload is present. */
		if (rwp->rw_endpoint &&
		    rwp->rw_crypto &&
		    fip->length) {
			walk.dbp = fip->data;
			walk.data = fip->data->data;
			walk.length = walk.left = fip->length;
			dbp = sec_decode_serialized_data (&walk,
							  0,
							  rwp->rw_crypto,
							  &fip->length,
							  &ofs,
							  (DDS_ReturnCode_t *) &error);
			if (!dbp)
				return;

			fip->data = dbp;
		}
		else
#endif
			ofs = 0;

		/* Cleanup the context. */
		ncp->c_db = fip->data;
		ncp->c_length = fip->length;
		ncp->c_data = fip->data->data + ofs;
		rcl_access (fip->data);
		fip->data->nrefs++;
		rcl_done (fip->data);
		cp = ncp;
		FRAGSC_TMR_STOP (rwp, &fip->timer);
		rfraginfo_delete (refp);
		mds_pool_free (&rtps_mem_blocks [MB_CCREF], refp);
		LIST_INIT (rwp->rw_changes);
		rwp->rw_changes.nchanges = 0;
	}
	else {
#endif
		STATS_INC (rwp->rw_ndata);

#ifdef RTPS_FRAGMENTS
		if (fip) { /* Lingering fragment?  Cleanup context. */
			FRAGSC_TMR_STOP (rwp, &fip->timer);
			rfraginfo_delete (refp);
			mds_pool_free (&rtps_mem_blocks [MB_CCREF], refp);
			LIST_INIT (rwp->rw_changes);
			rwp->rw_changes.nchanges = 0;
		}
	}
#endif
	RW_SIGNAL (rwp, "BE-Data");

#ifdef RTPS_MARKERS
	if (rp->endpoint.mark_data)
		rtps_marker_notify (r, EM_DATA, "sfr_be_data");
#endif
	if (SEQNR_LT (*cpsnr, rwp->rw_seqnr_next))
		return;

	if (!SEQNR_EQ (*cpsnr, rwp->rw_seqnr_next))
		dcps_samples_lost (r, SEQNR_DELTA (rwp->rw_seqnr_next, *cpsnr));

	if (!ignore) {
		if (cp->c_nrefs > 1) {
			ncp = hc_change_clone (cp);
			if (!ncp) {
				warn_printf ("sfr_be_data: out of memory for change clone!\r\n");
				return;
			}
		}
		else {
			rcl_access (cp);
			cp->c_nrefs++;
			rcl_done (cp);
			ncp = cp;
		}
		ncp->c_seqnr = *cpsnr;
		reader_cache_add_key (rp, ncp, hp, key, keylen);
	}
	rwp->rw_seqnr_next = cp->c_seqnr;
	SEQNR_INC (rwp->rw_seqnr_next);
	prof_stop (rtps_br_data, 1);
	RW_SNR_TRACE (rwp, "BE-data: update");

#ifdef RTPS_FRAGMENTS
	return;

    no_frag_mem:
	warn_printf ("sfr_be_data: no memory for fragment info!");
	LIST_INIT (rwp->rw_changes);
	rwp->rw_changes.nchanges = 0;
	hc_change_free (refp->u.c.change);
	mds_pool_free (&rtps_mem_blocks [MB_CCREF], refp);
    no_ref_mem:
    	hc_change_free (ncp);
#endif
}
Пример #11
0
static void sfr_rel_data (RemWriter_t         *rwp,
			  Change_t            *cp,
			  SequenceNumber_t    *cpsnr,
			  const KeyHash_t     *hp,
			  const unsigned char *key,
			  size_t              keylen,
#ifdef RTPS_FRAGMENTS
			  DataFragSMsg        *fragp,
			  FragInfo_t	      **finfo,
#endif
			  int                 ignore)
{
	ENDPOINT	*ep = &rwp->rw_reader->endpoint;
	CCREF		*rp, *gap_rp;
	HCI		hci;
	Change_t	*ncp;
	InstanceHandle	h;
	SequenceNumber_t gap_first, gap_last, seqnr_first, seqnr_last;
	RejectCause_t	cause;
	/*unsigned	max;*/
	int		error, ooo;

	ctrc_printd (RTPS_ID, RTPS_SFR_REL_DATA, &rwp, sizeof (rwp));
	prof_start (rtps_rr_data);
#if defined (RTPS_FRAGMENTS) && defined (EXTRA_STATS)
	if (fragp)
		STATS_INC (rwp->rw_ndatafrags);
	else
#endif
		STATS_INC (rwp->rw_ndata);

	RW_SIGNAL (rwp, "REL-Data");

#ifdef RTPS_MARKERS
	if (rwp->rw_reader->endpoint.mark_data)
		rtps_marker_notify (rwp->rw_reader->endpoint.endpoint, EM_DATA, "sfr_rel_data");
#endif
#ifdef RTPS_INIT_ACKNACK

	/* If first Data: accept it and become alive. */
	if (!rwp->rw_peer_alive)
		sfr_rel_alive (rwp);
#endif
	rwp->rw_hb_no_data = 0;

	/* If seqnr already in cache: ignore sample. */
	if (SEQNR_LT (*cpsnr, rwp->rw_seqnr_next)) {
		RW_SIGNAL (rwp, "REL-Data: ignore (SNR < hcache.SNR)");
		return;
	}
	ooo = !SEQNR_EQ (rwp->rw_seqnr_next, *cpsnr);

	/* Create a new instance already, if possible. */
	if (ignore
#ifdef RTPS_FRAGMENTS
	           || fragp
#endif
		           )
		hci = NULL;
	else if (ep->multi_inst) {
		hci = hc_lookup_hash (ep->endpoint->cache, hp, key, keylen,
					  &h, 1, ooo, &cause);
		if (!hci) {
			/* Don't see this as rejected -- since there is no ack
			   given, sample will be retransmitted!
			dcps_sample_rejected ((Reader_t *) ep->endpoint,
					      (DDS_SampleRejectedStatusKind) cause,
					      h);*/
			if (!ooo)
				rwp->rw_blocked = 1;
			return;
		}
	}
	else {
		if (!hc_accepts (ep->endpoint->cache, ooo)) {
			/* Don't see this as rejected -- since there is no ack
			   given, sample will be retransmitted!
			dcps_sample_rejected ((Reader_t *) ep->endpoint,
					      DDS_REJECTED_BY_SAMPLES_LIMIT, 0);*/
			if (!ooo)
				rwp->rw_blocked = 1;
			return;
		}
		h = 0;
		hci = NULL;
	}

	/* Add to changes list of proxy writer context. */
	if (LIST_NONEMPTY (rwp->rw_changes)) { /* Already some samples queued. */

		RW_SIGNAL (rwp, "REL-Data: changes-queued");

		if (hci)
			hc_inst_inform (ep->endpoint->cache, hci);

		if (rwp->rw_changes.head->relevant)
			seqnr_first = rwp->rw_changes.head->u.c.change->c_seqnr;
		else
			seqnr_first = rwp->rw_changes.head->u.range.first;
		if (rwp->rw_changes.tail->relevant)
			seqnr_last = rwp->rw_changes.tail->u.c.change->c_seqnr;
		else
			seqnr_last = rwp->rw_changes.tail->u.range.last;

		if (SEQNR_GT (*cpsnr, seqnr_last)) { /* Seqnr > last in list! */

			/* Add new data sample node after tail of list. */
			RW_SIGNAL (rwp, "REL-Data: add-missing>last");
			gap_first = seqnr_last;
			SEQNR_INC (gap_first);
			if (!SEQNR_EQ (gap_first, *cpsnr)) {

				/* Gap between tail and data: add MISSING sample
				   node between current tail of list and to be
				   added data sample node. */
				gap_last = *cpsnr;
				SEQNR_DEC (gap_last);
				if (!rwp->rw_changes.tail->relevant &&
				    rwp->rw_changes.tail->state == CS_MISSING) {

					/* Extend previous gap node. */
					rwp->rw_changes.tail->u.range.last = gap_last;
					RW_SIGNAL (rwp, "REL-Data: extend-gap");
				}
				else {	/* Add new gap node. */
					gap_rp = ccref_add_gap (&rwp->rw_changes,
							        &gap_first,
							        &gap_last,
							        1,
								CS_MISSING);
					if (!gap_rp)
						return;

					RW_SIGNAL (rwp, "REL-Data: add-gap");
				}
			}

			/* Append data sample node. */
			if (ignore)
				rp = ccref_add_gap (&rwp->rw_changes, cpsnr,
						    cpsnr, 1, CS_RECEIVED);
			else
				rp = ccref_add_received (&rwp->rw_changes, cp,
							 cpsnr, hci, h, 1);
			if (!rp)
				return;

			rwp->rw_reader->data_queued++;
#ifdef RTPS_FRAGMENTS
			if (fragp) {
				sfr_fragment (rwp, rp, fragp, finfo, hp, cp, ooo, ignore);
				return;
			}
#endif
			RANGE_CHECK (&rwp->rw_changes, "REL-Data: >last");
		}
		else if (SEQNR_LT (*cpsnr, seqnr_first)) { /* Seqnr < first! */

			/* Add new data sample node before head of list. */
			RW_SIGNAL (rwp, "REL-Data: add-missing<first");
			gap_last = seqnr_first;
			SEQNR_DEC (gap_last);
			if (!SEQNR_EQ (gap_last, *cpsnr)) {

				/* Gap between data and head: add MISSING sample
				   node between to be added data sample and
				   current head of list. */
				gap_first = *cpsnr;
				SEQNR_INC (gap_first);
				if (!rwp->rw_changes.head->relevant &&
				    rwp->rw_changes.tail->state == CS_MISSING) {

					/* Extended following gap node. */
					rwp->rw_changes.head->u.range.first = gap_first;
					RW_SIGNAL (rwp, "REL-Data: extend-gap");
				}
				else {	/* Add new gap node. */
					gap_rp = ccref_add_gap (&rwp->rw_changes,
							        &gap_first,
							        &gap_last,
							        0,
								CS_MISSING);
					if (!gap_rp)
						return;

					RW_SIGNAL (rwp, "REL-Data: add-gap");
				}
			}

			/* Prepend data sample node. */
			if (ignore)
				rp = ccref_add_gap (&rwp->rw_changes, cpsnr,
							 cpsnr, 0, CS_RECEIVED);
			else
				rp = ccref_add_received (&rwp->rw_changes, cp,
							 cpsnr, hci, h, 0);
			if (!rp)
				return;

			rwp->rw_reader->data_queued++;
#ifdef RTPS_FRAGMENTS
			if (fragp) {
				sfr_fragment (rwp, rp, fragp, finfo, hp, cp, ooo, ignore);
				return;
			}
#endif
			RANGE_CHECK (&rwp->rw_changes, "REL-Data: <first");
		}
		else {	/* Seqnr somewhere in list - lets find it. */
			LIST_FOREACH (rwp->rw_changes, rp)
				if (rp->relevant) {
					if (SEQNR_EQ (rp->u.c.change->c_seqnr, *cpsnr))
						break;
				}
				else if (!SEQNR_LT (*cpsnr, rp->u.range.first) &&
					 !SEQNR_GT (*cpsnr, rp->u.range.last))
					break;

			if (LIST_END (rwp->rw_changes, rp) ||
			    (rp->state != CS_MISSING 
#ifdef RTPS_FRAGMENTS
			       && !rp->fragments
#endif
			       			))
				return;
#ifdef RTPS_FRAGMENTS
			if (rp->fragments) {
				sfr_fragment (rwp, rp, fragp, finfo, hp, cp, ooo, ignore);
				return;
			}
#endif
			RW_SIGNAL (rwp, "REL-Data: in-range");
			RANGE_CHECK (&rwp->rw_changes, "REL-Data: in-range");
			if (SEQNR_GT (*cpsnr, rp->u.range.first)) {

				/* Prepend gap node: range.first .. *cpsnr -1 */
				gap_first = rp->u.range.first;
				gap_last = *cpsnr;
				SEQNR_DEC (gap_last);
				gap_rp = ccref_insert_gap (&rwp->rw_changes,
						           rp->prev,
						           &gap_first,
						           &gap_last,
							   CS_MISSING);
				if (!gap_rp)
					return;

				RW_SIGNAL (rwp, "REL-Data: prepend-gap");
			}
			if (SEQNR_LT (*cpsnr, rp->u.range.last)) {

				/* Append gap node: *cpsnr + 1 .. range.last */
				gap_first = *cpsnr;
				SEQNR_INC (gap_first);
				gap_last = rp->u.range.last;
				gap_rp = ccref_insert_gap (&rwp->rw_changes,
						           rp,
						           &gap_first,
						           &gap_last,
							   CS_MISSING);
				if (!gap_rp)
					return;

				RW_SIGNAL (rwp, "REL-Data: append-gap");
			}

			/* Reuse gap node for data. */
#ifdef RTPS_FRAGMENTS
			rp->fragments = NULL;
#endif
			if (ignore) {
				rp->relevant = 0;
				rp->u.range.first = *cpsnr;
				rp->u.range.last = *cpsnr;
			}
			else {
				if (cp->c_nrefs > 1) {
					ncp = hc_change_clone (cp);
					if (!ncp) {
						warn_printf ("sfr_rel_data (): out of memory for change clone!\r\n");
						return;
					}
				}
				else {
					rcl_access (cp);
					cp->c_nrefs++;
					rcl_done (cp);
					ncp = cp;
				}
				ncp->c_handle = h;
				ncp->c_seqnr = *cpsnr;
				rp->relevant = 1;
				rp->u.c.hci = hci;
				rp->u.c.change = ncp;
				rwp->rw_reader->data_queued++;
			}
			rp->state = CS_RECEIVED;
#ifdef RTPS_FRAGMENTS
			if (fragp) {
				sfr_fragment (rwp, rp, fragp, finfo, hp, cp, ooo, ignore);
				return;
			}
#endif
			RW_SIGNAL (rwp, "REL-Data: missing::received");
			RANGE_CHECK (&rwp->rw_changes, "REL-Data: missing::received");
		}

		/* If the received data sample caused some samples to be valid,
		   add these samples to the history cache and remove them from
		   the changes list. */
		rp = LIST_HEAD (rwp->rw_changes);
		if (rp &&
		    (rp->state == CS_RECEIVED || rp->state == CS_LOST) &&
#ifdef RTPS_FRAGMENTS
		    !rp->fragments &&
#endif
		    rwp->rw_heartbeats) {
			RW_SIGNAL (rwp, "REL-Data: process-samples");
			RANGE_CHECK (&rwp->rw_changes, "REL-Data: process-samples");
			sfr_process_samples (rwp);
		}
	}