/*
This file is part of GNU APL, a free implementation of the
ISO/IEC Standard 13751, "Programming Language APL, Extended"
Copyright (C) 2008-2015 Dr. Jürgen Sauermann
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include /* For O_* constants */
#include
#include
#include
#include
#include
#include
#include
#include
#include "Backtrace.hh"
#include "Logging.hh"
#include "PrintOperator.hh"
#include "ProcessorID.hh"
#include "Svar_record.hh"
#include "Svar_signals.hh"
extern ostream & get_CERR();
//=============================================================================
const char *
event_name(Svar_event ev)
{
switch(ev)
{
case SVE_NO_EVENTS: return "(no event)";
case SVE_OFFER_MISMATCH: return "offer mismatch";
case SVE_OFFER_MATCHED: return "offer matched";
case SVE_OFFER_RETRACT: return "offer retracted";
case SVE_ACCESS_CONTROL: return "access control changed";
case SVE_USE_BY_OFF_SUCCESS: return "use by offering successful";
case SVE_USE_BY_OFF_FAILED: return "use by offering failed";
case SVE_SET_BY_OFF_SUCCESS: return "set by offering successful";
case SVE_SET_BY_OFF_FAILED: return "set by offering failed";
case SVE_USE_BY_ACC_SUCCESS: return "use by accepting successful";
case SVE_USE_BY_ACC_FAILED: return "use by accepting failed";
case SVE_SET_BY_ACC_SUCCESS: return "set by accepting successful";
case SVE_SET_BY_ACC_FAILED: return "set by accepting failed";
}
return "(unknown event)";
}
//-----------------------------------------------------------------------------
ostream &
Svar_partner::print(ostream & out) const
{
out << setw(5) << id.proc;
if (id.parent) out << "," << left << setw(5) << id.parent << right;
else out << " ";
out << "│" << setw(3) << tcp_fd << "│"
<< hex << uppercase << setfill('0') << setw(2) << flags
<< dec << nouppercase << setfill(' ');
return out;
}
//-----------------------------------------------------------------------------
void
Svar_record::remove_accepting()
{
accepting.clear();
}
//-----------------------------------------------------------------------------
void
Svar_record::remove_offering()
{
const AP_num3 offered_to = offering.id;
offering = accepting;
accepting.clear();
accepting.id = offered_to;
}
//-----------------------------------------------------------------------------
SV_Coupling
Svar_record::retract()
{
const SV_Coupling old_coupling = get_coupling();
if (ProcessorID::get_id() == offering.id) remove_offering();
else if (ProcessorID::get_id() == accepting.id) remove_accepting();
else
{
bad_proc("Svar_record::retract", ProcessorID::get_id());
return NO_COUPLING;
}
// clear variable if last partner has retracted, or else send signal
// to the remaining partner.
//
if (get_coupling() == NO_COUPLING) // retract of a non-coupled variable
{
clear();
}
return old_coupling;
}
//-----------------------------------------------------------------------------
bool
Svar_record::is_ws_to_ws() const
{
if (get_coupling() != SV_COUPLED) return offering.id.proc >= AP_FIRST_USER;
return offering.id.proc >= AP_FIRST_USER &&
accepting.id.proc >= AP_FIRST_USER;
}
//-----------------------------------------------------------------------------
bool
Svar_record::match_name(const uint32_t * UCS_other) const
{
// compare INCLUDING terminating 0;
for (int v = 0; v < (MAX_SVAR_NAMELEN); ++v)
{
if (varname[v] != UCS_other[v])
{
if (varname[v] == 0x22C6) return true; // ⋆
if (varname[v] == '*') return true;
if (UCS_other[v] == 0x22C6) return true; // ⋆
if (UCS_other[v] == '*') return true;
return false;
}
if (varname[v] == 0) return true; // short name
}
return true; // long name
}
//-----------------------------------------------------------------------------
Svar_Control
mirror(int flags)
{
int ret = 0;
if (flags & SET_BY_OFF) ret |= SET_BY_ACC;
if (flags & SET_BY_ACC) ret |= SET_BY_OFF;
if (flags & USE_BY_OFF) ret |= USE_BY_ACC;
if (flags & USE_BY_ACC) ret |= USE_BY_OFF;
return Svar_Control(ret);
}
//-----------------------------------------------------------------------------
Svar_Control
Svar_record::get_control() const
{
int ctl = offering.get_control() | accepting.get_control();
if (ProcessorID::get_id() == accepting.id) ctl = mirror(ctl);
return Svar_Control(ctl);
}
//-----------------------------------------------------------------------------
void
Svar_record::set_control(Svar_Control ctl)
{
Log(LOG_shared_variables)
{
get_CERR() << "set_control(" << ctl << ") on ";
print_name(get_CERR());
get_CERR() << " by " << ProcessorID::get_id().proc << endl;
}
if (ProcessorID::get_id() == offering.id)
{
offering.set_control(ctl);
if (get_coupling() == SV_COUPLED) // fully coupled: inform peer
{
accepting.events = Svar_event
(accepting.events | SVE_ACCESS_CONTROL);
}
}
else if (ProcessorID::get_id() == accepting.id) // hence fully coupled
{
accepting.set_control(mirror(ctl));
offering.events = Svar_event(offering.events | SVE_ACCESS_CONTROL);
}
else
{
bad_proc(__FUNCTION__, ProcessorID::get_id());
}
}
//-----------------------------------------------------------------------------
Svar_state
Svar_record::get_state() const
{
return state;
}
//-----------------------------------------------------------------------------
void
Svar_record::set_state(bool used, const char * loc)
{
usleep(50000);
Log(LOG_shared_variables)
{
const char * op = used ? "used" : "set";
get_CERR() << "set_state(" << op << ") on ";
print_name(get_CERR());
get_CERR() << " by " << ProcessorID::get_id().proc
<< " at " << loc << endl;
}
// the control vector as seen by the offering side
//
const int control = offering.get_control() | accepting.get_control();
Svar_event event = SVE_NO_EVENTS;
Svar_partner * peer = 0;
if (ProcessorID::get_id() == offering.id)
{
peer = &offering;
offering.events = SVE_NO_EVENTS; // clear events
if (used) // offering has used the variable (unless read-back)
{
if (state == SVS_OFF_HAS_SET) ; // read-back
else
{
if (control & USE_BY_OFF) event = SVE_USE_BY_OFF_SUCCESS;
state = SVS_IDLE;
}
}
else // offering has set the variable
{
if (control & SET_BY_OFF) event = SVE_SET_BY_OFF_SUCCESS;
state = SVS_OFF_HAS_SET;
}
}
else if (ProcessorID::get_id() == accepting.id)
{
peer = &offering;
accepting.events = SVE_NO_EVENTS; // clear events
if (used) // accepting has used the variable (unless read-back)
{
if (state == SVS_ACC_HAS_SET) ; // read-back
else
{
if (control & USE_BY_ACC) event = SVE_USE_BY_ACC_SUCCESS;
state = SVS_IDLE;
}
}
else // accepting has set the variable
{
if (control & SET_BY_ACC) event = SVE_SET_BY_ACC_SUCCESS;
state = SVS_ACC_HAS_SET;
}
}
else // only the partners should call set_state();
{
bad_proc(__FUNCTION__, ProcessorID::get_id());
return;
}
// if access was restricted then inform peer
//
if (peer && event != SVE_NO_EVENTS)
{
peer->events = Svar_event(peer->events | event);
}
}
//-----------------------------------------------------------------------------
bool
Svar_record::may_use(int attempt)
{
// control restriction as seen by the offering partner
//
const int control = offering.get_control() | accepting.get_control();
const int restriction = control & state;
if (ProcessorID::get_id() == offering.id)
{
if ((restriction & USE_BY_OFF) == 0) return true; // no restriction
if (accepting.is_active() && (attempt == 0))
{
accepting.events = Svar_event(accepting.events |
SVE_USE_BY_OFF_FAILED);
}
return false;
}
if (ProcessorID::get_id() == accepting.id)
{
if ((restriction & USE_BY_ACC) == 0) return true; // no restriction
if (offering.is_active() && (attempt == 0)) // maybe send event to peer
{
offering.events = Svar_event(offering.events |
SVE_USE_BY_ACC_FAILED);
}
return false;
}
bad_proc(__FUNCTION__, ProcessorID::get_id());
return false;
}
//-----------------------------------------------------------------------------
bool
Svar_record::may_set(int attempt)
{
// control restriction as seen by the offering partner
//
const int control = offering.get_control() | accepting.get_control();
const int restriction = control & state;
if (ProcessorID::get_id() == offering.id)
{
if ((restriction & SET_BY_OFF) == 0) return true; // no restriction
if (accepting.is_active() && (attempt == 0)) // maybe send event to peer
{
accepting.events = Svar_event(accepting.events |
SVE_SET_BY_OFF_FAILED);
}
return false;
}
if (ProcessorID::get_id() == accepting.id)
{
if ((restriction & SET_BY_ACC) == 0) return true; // no restriction
if (offering.is_active() && (attempt == 0)) // maybe send event to peer
{
offering.events = Svar_event(offering.events |
SVE_SET_BY_ACC_FAILED);
}
return false;
}
bad_proc(__FUNCTION__, ProcessorID::get_id());
return false;
}
//-----------------------------------------------------------------------------
void
Svar_record::bad_proc(const char * function, const AP_num3 & id) const
{
get_CERR() << function << "(): proc " << id.proc
<< " does not match offering proc " << offering.id.proc
<< " nor accepting proc " << accepting.id.proc << endl;
}
//-----------------------------------------------------------------------------
void
Svar_record::print(ostream & out) const
{
const Svar_state st = get_state();
out << "║" << setw(5) << (key & 0xFFFF) << "│" << get_coupling() << "║";
offering.print(out) << "║";
accepting.print(out) << "║";
if (st & SET_BY_OFF) out << "1"; else out << "0";
if (st & SET_BY_ACC) out << "1"; else out << "0";
if (st & USE_BY_OFF) out << "1"; else out << "0";
if (st & USE_BY_ACC) out << "1│"; else out << "0│";
print_name(out, varname, 10) << "║" << endl;
}
//-----------------------------------------------------------------------------
ostream &
Svar_record::print_name(ostream & out, const uint32_t * name, int len)
{
while (*name)
{
uint32_t uni = *name++;
if (uni < 0x80)
{
out << char(uni);
}
else if (uni < 0x800)
{
const uint8_t b1 = uni & 0x3F; uni >>= 6;
out << char(uni | 0xC0)
<< char(b1 | 0x80);
}
else if (uni < 0x10000)
{
const uint8_t b2 = uni & 0x3F; uni >>= 6;
const uint8_t b1 = uni & 0x3F; uni >>= 6;
out << char(uni | 0xE0)
<< char(b1 | 0x80)
<< char(b2 | 0x80);
}
else if (uni < 0x110000)
{
const uint8_t b3 = uni & 0x3F; uni >>= 6;
const uint8_t b2 = uni & 0x3F; uni >>= 6;
const uint8_t b1 = uni & 0x3F; uni >>= 6;
out << char(uni | 0xE0)
<< char(b1 | 0x80)
<< char(b2 | 0x80)
<< char(b3 | 0x80);
}
--len;
}
while (len-- > 0) out << " ";
return out;
}
//-----------------------------------------------------------------------------