<commit_before>/// \file conn_http_dynamic.cpp
/// Contains the main code for the HTTP Dynamic Connector

#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <getopt.h>
#include <ctime>
#include <mist/socket.h>
#include <mist/http_parser.h>
#include <mist/json.h>
#include <mist/dtsc.h>
#include <mist/flv_tag.h>
#include <mist/base64.h>
#include <mist/amf.h>
#include <mist/mp4.h>
#include <mist/config.h>
#include <sstream>
#include <mist/stream.h>

/// Holds everything unique to HTTP Dynamic Connector.
namespace Connector_HTTP{

  std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata){
    MP4::AFRT afrt;
    afrt.SetUpdate(false);
    afrt.SetTimeScale(1000);
    afrt.AddQualityEntry("");
    if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){
      //metadata["lasttime"].asInt()?
      afrt.AddFragmentRunEntry(1, 0, 2000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
    }else{
      afrt.AddFragmentRunEntry(1, 0, metadata["video"]["keyms"].asInt()); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
    }
    afrt.WriteContent();

    MP4::ASRT asrt;
    asrt.SetUpdate(false);
    asrt.AddQualityEntry("");
    asrt.AddSegmentRunEntry(1, 199);//1 Segment, 199 Fragments
    asrt.WriteContent();

    MP4::ABST abst;
    abst.AddFragmentRunTable(&afrt);
    abst.AddSegmentRunTable(&asrt);
    abst.SetBootstrapVersion(1);
    abst.SetProfile(0);
    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
      abst.SetLive(false);
      abst.SetMediaTime(1000*metadata["length"].asInt());
    }else{
      abst.SetLive(true);
      abst.SetMediaTime(0xFFFFFFFF);//metadata["lasttime"].asInt()?
    }
    abst.SetUpdate(false);
    abst.SetTimeScale(1000);
    abst.SetSMPTE(0);
    abst.SetMovieIdentifier(MovieId);
    abst.SetDRM("");
    abst.SetMetaData("");
    abst.AddServerEntry("");
    abst.AddQualityEntry("");
    abst.WriteContent();

    std::string Result;
    Result.append((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize());
    #if DEBUG >= 4
    std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl;
    #endif
    return Base64::encode(Result);
  }

  /// Returns a F4M-format manifest file
  std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){
    std::string Result;
    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
      std::stringstream st;
      st << ((double)metadata["video"]["keyms"].asInt() / 1000);
      Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
      "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"
      "<id>" + MovieId + "</id>\n"
      "<duration>" + metadata["length"].asString() + "</duration>\n"
      "<mimeType>video/mp4</mimeType>\n"
      "<streamType>recorded</streamType>\n"
      "<deliveryType>streaming</deliveryType>\n"
      "<bestEffortFetchInfo segmentDuration=\""+metadata["length"].asString()+".000\" fragmentDuration=\""+st.str()+"\" />\n"
      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + GenerateBootstrap(MovieId, metadata) + "</bootstrapInfo>\n"
      "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
      "</manifest>\n";
    }else{
      Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
      "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"
      "<id>" + MovieId + "</id>\n"
      "<mimeType>video/mp4</mimeType>\n"
      "<streamType>live</streamType>\n"
      "<deliveryType>streaming</deliveryType>\n"
      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + GenerateBootstrap(MovieId, metadata) + "</bootstrapInfo>\n"
      "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
      "</manifest>\n";
    }
    #if DEBUG >= 4
    std::cerr << "Sending this manifest:" << std::endl << Result << std::endl;
    #endif
    return Result;
  }//BuildManifest

  /// Main function for Connector_HTTP_Dynamic
  int Connector_HTTP_Dynamic(Socket::Connection conn){
    std::string FlashBuf;
    int flashbuf_nonempty = 0;
    FLV::Tag tmp;//temporary tag, for init data

    std::queue<std::string> Flash_FragBuffer;//Fragment buffer
    DTSC::Stream Strm;//Incoming stream buffer.
    HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.

    bool ready4data = false;//Set to true when streaming is to begin.
    bool pending_manifest = false;
    bool inited = false;
    Socket::Connection ss(-1);
    std::string streamname;
    FLV::Tag tag;///< Temporary tag buffer.
    std::string recBuffer = "";

    std::string Quality;
    int Segment = -1;
    int ReqFragment = -1;
    int temp;
    int Flash_RequestPending = 0;
    unsigned int lastStats = 0;
    conn.setBlocking(false);//do not block on conn.spool() when no data is available

    while (conn.connected()){
      if (conn.spool()){
        if (HTTP_R.Read(conn.Received())){
          #if DEBUG >= 4
          std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
          #endif
          conn.setHost(HTTP_R.GetHeader("X-Origin"));
          if (HTTP_R.url.find("f4m") == std::string::npos){
            streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1);
            if (!ss){
              ss = Util::Stream::getStream(streamname);
              if (!ss.connected()){
                #if DEBUG >= 1
                fprintf(stderr, "Could not connect to server!\n");
                #endif
                ss.close();
                HTTP_S.Clean();
                HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
                conn.Send(HTTP_S.BuildResponse("404", "Not found"));
                ready4data = false;
                continue;
              }
              ss.setBlocking(false);
              inited = true;
            }
            Quality = HTTP_R.url.substr( HTTP_R.url.find("/",1)+1 );
            Quality = Quality.substr(0, Quality.find("Seg"));
            temp = HTTP_R.url.find("Seg") + 3;
            Segment = atoi( HTTP_R.url.substr(temp,HTTP_R.url.find("-",temp)-temp).c_str());
            temp = HTTP_R.url.find("Frag") + 4;
            ReqFragment = atoi( HTTP_R.url.substr(temp).c_str() );
            #if DEBUG >= 4
            printf( "Quality: %s, Seg %d Frag %d\n", Quality.c_str(), Segment, ReqFragment);
            #endif
            std::stringstream sstream;
            sstream << "f " << ReqFragment << "\no \n";
            ss.Send(sstream.str().c_str());
            ss.flush();
            Flash_RequestPending++;
          }else{
            streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1);
            if (!Strm.metadata.isNull()){
              HTTP_S.Clean();
              HTTP_S.SetHeader("Content-Type","text/xml");
              HTTP_S.SetHeader("Cache-Control","no-cache");
              std::string manifest = BuildManifest(streamname, Strm.metadata);
              HTTP_S.SetBody(manifest);
              conn.Send(HTTP_S.BuildResponse("200", "OK"));
              #if DEBUG >= 3
              printf("Sent manifest\n");
              #endif
              pending_manifest = false;
            }else{
              pending_manifest = true;
            }
          }
          ready4data = true;
          HTTP_R.Clean(); //clean for any possible next requests
        }else{
          #if DEBUG >= 3
          fprintf(stderr, "Could not parse the following:\n%s\n", conn.Received().c_str());
          #endif
        }
      }
      if (ready4data){
        if (!inited){
          //we are ready, connect the socket!
          ss = Util::Stream::getStream(streamname);
          if (!ss.connected()){
            #if DEBUG >= 1
            fprintf(stderr, "Could not connect to server!\n");
            #endif
            ss.close();
            HTTP_S.Clean();
            HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
            conn.Send(HTTP_S.BuildResponse("404", "Not found"));
            ready4data = false;
            continue;
          }
          ss.setBlocking(false);
          #if DEBUG >= 3
          fprintf(stderr, "Everything connected, starting to send video data...\n");
          #endif
          inited = true;
        }
        if ((Flash_RequestPending > 0) && !Flash_FragBuffer.empty()){
          HTTP_S.Clean();
          HTTP_S.SetHeader("Content-Type","video/mp4");
          HTTP_S.SetBody(MP4::mdatFold(Flash_FragBuffer.front()));
          Flash_FragBuffer.pop();
          conn.Send(HTTP_S.BuildResponse("200", "OK"));
          Flash_RequestPending--;
          #if DEBUG >= 3
          fprintf(stderr, "Sending a video fragment. %i left in buffer, %i requested\n", (int)Flash_FragBuffer.size(), Flash_RequestPending);
          #endif
        }
        unsigned int now = time(0);
        if (now != lastStats){
          lastStats = now;
          ss.Send("S ");
          ss.Send(conn.getStats("HTTP_Dynamic").c_str());
        }
        if (ss.spool() || ss.Received() != ""){
          if (Strm.parsePacket(ss.Received())){
            if (Strm.getPacket(0).isMember("time")){
              if (!Strm.metadata.isMember("firsttime")){
                Strm.metadata["firsttime"] = Strm.getPacket(0)["time"];
              }else{
                if (!Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){
                  Strm.getPacket(0)["time"] = Strm.getPacket(0)["time"].asInt() - Strm.metadata["firsttime"].asInt();
                }
              }
              Strm.metadata["lasttime"] = Strm.getPacket(0)["time"];
            }
            if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
              tag.DTSCLoader(Strm);
            }
            if (pending_manifest){
              HTTP_S.Clean();
              HTTP_S.SetHeader("Content-Type","text/xml");
              HTTP_S.SetHeader("Cache-Control","no-cache");
              std::string manifest = BuildManifest(streamname, Strm.metadata);
              HTTP_S.SetBody(manifest);
              conn.Send(HTTP_S.BuildResponse("200", "OK"));
              #if DEBUG >= 3
              printf("Sent manifest\n");
              #endif
              pending_manifest = false;
            }
            if (Strm.getPacket(0).isMember("keyframe") || Strm.getPacket(0)["datatype"].asString() == "pause_marker"){
              if (flashbuf_nonempty){
                Flash_FragBuffer.push(FlashBuf);
                while (Flash_FragBuffer.size() > 2){
                  Flash_FragBuffer.pop();
                }
                #if DEBUG >= 4
                fprintf(stderr, "Received a %s fragment of %i packets. Now %i in buffer.\n", Strm.getPacket(0)["datatype"].asString().c_str(), flashbuf_nonempty, (int)Flash_FragBuffer.size());
                #endif
              }
              FlashBuf.clear();
              flashbuf_nonempty = 0;
              //fill buffer with init data, if needed.
              if (Strm.metadata.isMember("audio") && Strm.metadata["audio"].isMember("init")){
                tmp.DTSCAudioInit(Strm);
                FlashBuf.append(tmp.data, tmp.len);
              }
              if (Strm.metadata.isMember("video") && Strm.metadata["video"].isMember("init")){
                tmp.DTSCVideoInit(Strm);
                FlashBuf.append(tmp.data, tmp.len);
              }
            }
            if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
              ++flashbuf_nonempty;
              FlashBuf.append(tag.data, tag.len);
            }
          }else{
            if (pending_manifest && !Strm.metadata.isNull()){
              HTTP_S.Clean();
              HTTP_S.SetHeader("Content-Type","text/xml");
              HTTP_S.SetHeader("Cache-Control","no-cache");
              std::string manifest = BuildManifest(streamname, Strm.metadata);
              HTTP_S.SetBody(manifest);
              conn.Send(HTTP_S.BuildResponse("200", "OK"));
              #if DEBUG >= 3
              printf("Sent manifest\n");
              #endif
              pending_manifest = false;
            }
          }
        }
        if (!ss.connected()){break;}
      }
    }
    conn.close();
    ss.Send("S ");
    ss.Send(conn.getStats("HTTP_Dynamic").c_str());
    ss.flush();
    ss.close();
    #if DEBUG >= 1
    if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());}
    fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
    if (inited){
      fprintf(stderr, "Status was: inited\n");
    }else{
      if (ready4data){
        fprintf(stderr, "Status was: ready4data\n");
      }else{
        fprintf(stderr, "Status was: connected\n");
      }
    }
    #endif
    return 0;
  }//Connector_HTTP_Dynamic main function

};//Connector_HTTP_Dynamic namespace

int main(int argc, char ** argv){
  Util::Config conf(argv[0], PACKAGE_VERSION);
  conf.addConnectorOptions(1935);
  conf.parseArgs(argc, argv);
  Socket::Server server_socket = Socket::Server("/tmp/mist/http_dynamic");
  if (!server_socket.connected()){return 1;}
  conf.activate();

  while (server_socket.connected() && conf.is_active){
    Socket::Connection S = server_socket.accept();
    if (S.connected()){//check if the new connection is valid
      pid_t myid = fork();
      if (myid == 0){//if new child, start MAINHANDLER
        return Connector_HTTP::Connector_HTTP_Dynamic(S);
      }else{//otherwise, do nothing or output debugging text
        #if DEBUG >= 3
        fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
        #endif
      }
    }
  }//while connected
  server_socket.close();
  return 0;
}//main
<commit_msg>Working HTTP Dynamic VoD<commit_after>/// \file conn_http_dynamic.cpp
/// Contains the main code for the HTTP Dynamic Connector

#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <getopt.h>
#include <ctime>
#include <mist/socket.h>
#include <mist/http_parser.h>
#include <mist/json.h>
#include <mist/dtsc.h>
#include <mist/flv_tag.h>
#include <mist/base64.h>
#include <mist/amf.h>
#include <mist/mp4.h>
#include <mist/config.h>
#include <sstream>
#include <mist/stream.h>

/// Holds everything unique to HTTP Dynamic Connector.
namespace Connector_HTTP{

  std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata){
    MP4::AFRT afrt;
    afrt.SetUpdate(false);
    afrt.SetTimeScale(1000);
    afrt.AddQualityEntry("");
    if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){
      //metadata["lasttime"].asInt()?
      afrt.AddFragmentRunEntry(1, 0, 2000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
    }else{
      afrt.AddFragmentRunEntry(1, 0, metadata["video"]["keyms"].asInt()); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds
    }
    afrt.WriteContent();

    MP4::ASRT asrt;
    asrt.SetUpdate(false);
    asrt.AddQualityEntry("");
    asrt.AddSegmentRunEntry(1, 199);//1 Segment, 199 Fragments
    asrt.WriteContent();

    MP4::ABST abst;
    abst.AddFragmentRunTable(&afrt);
    abst.AddSegmentRunTable(&asrt);
    abst.SetBootstrapVersion(1);
    abst.SetProfile(0);
    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
      abst.SetLive(false);
      abst.SetMediaTime(1000*metadata["length"].asInt());
    }else{
      abst.SetLive(true);
      abst.SetMediaTime(0xFFFFFFFF);//metadata["lasttime"].asInt()?
    }
    abst.SetUpdate(false);
    abst.SetTimeScale(1000);
    abst.SetSMPTE(0);
    abst.SetMovieIdentifier(MovieId);
    abst.SetDRM("");
    abst.SetMetaData("");
    abst.AddServerEntry("");
    abst.AddQualityEntry("");
    abst.WriteContent();

    std::string Result;
    Result.append((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize());
    #if DEBUG >= 4
    std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl;
    #endif
    return Base64::encode(Result);
  }

  /// Returns a F4M-format manifest file
  std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){
    std::string Result;
    if (metadata.isMember("length") && metadata["length"].asInt() > 0){
      std::stringstream st;
      st << ((double)metadata["video"]["keyms"].asInt() / 1000);
      Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
      "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"
      "<id>" + MovieId + "</id>\n"
      "<duration>" + metadata["length"].asString() + "</duration>\n"
      "<mimeType>video/mp4</mimeType>\n"
      "<streamType>recorded</streamType>\n"
      "<deliveryType>streaming</deliveryType>\n"
      "<bestEffortFetchInfo segmentDuration=\""+metadata["length"].asString()+".000\" fragmentDuration=\""+st.str()+"\" />\n"
      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + GenerateBootstrap(MovieId, metadata) + "</bootstrapInfo>\n"
      "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
      "</manifest>\n";
    }else{
      Result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
      "<manifest xmlns=\"http://ns.adobe.com/f4m/1.0\">\n"
      "<id>" + MovieId + "</id>\n"
      "<mimeType>video/mp4</mimeType>\n"
      "<streamType>live</streamType>\n"
      "<deliveryType>streaming</deliveryType>\n"
      "<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + GenerateBootstrap(MovieId, metadata) + "</bootstrapInfo>\n"
      "<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\"></media>\n"
      "</manifest>\n";
    }
    #if DEBUG >= 4
    std::cerr << "Sending this manifest:" << std::endl << Result << std::endl;
    #endif
    return Result;
  }//BuildManifest

  /// Main function for Connector_HTTP_Dynamic
  int Connector_HTTP_Dynamic(Socket::Connection conn){
    std::string FlashBuf;
    int flashbuf_nonempty = 0;
    FLV::Tag tmp;//temporary tag, for init data

    std::queue<std::string> Flash_FragBuffer;//Fragment buffer
    DTSC::Stream Strm;//Incoming stream buffer.
    HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.

    bool ready4data = false;//Set to true when streaming is to begin.
    bool pending_manifest = false;
    bool receive_marks = false;//when set to true, this stream will ignore keyframes and instead use pause marks
    bool inited = false;
    Socket::Connection ss(-1);
    std::string streamname;
    FLV::Tag tag;///< Temporary tag buffer.
    std::string recBuffer = "";

    std::string Quality;
    int Segment = -1;
    int ReqFragment = -1;
    int temp;
    int Flash_RequestPending = 0;
    unsigned int lastStats = 0;
    conn.setBlocking(false);//do not block on conn.spool() when no data is available

    while (conn.connected()){
      if (conn.spool()){
        if (HTTP_R.Read(conn.Received())){
          #if DEBUG >= 4
          std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
          #endif
          conn.setHost(HTTP_R.GetHeader("X-Origin"));
          if (HTTP_R.url.find("f4m") == std::string::npos){
            streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1);
            if (!ss){
              ss = Util::Stream::getStream(streamname);
              if (!ss.connected()){
                #if DEBUG >= 1
                fprintf(stderr, "Could not connect to server!\n");
                #endif
                ss.close();
                HTTP_S.Clean();
                HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
                conn.Send(HTTP_S.BuildResponse("404", "Not found"));
                ready4data = false;
                continue;
              }
              ss.setBlocking(false);
              inited = true;
            }
            Quality = HTTP_R.url.substr( HTTP_R.url.find("/",1)+1 );
            Quality = Quality.substr(0, Quality.find("Seg"));
            temp = HTTP_R.url.find("Seg") + 3;
            Segment = atoi( HTTP_R.url.substr(temp,HTTP_R.url.find("-",temp)-temp).c_str());
            temp = HTTP_R.url.find("Frag") + 4;
            ReqFragment = atoi( HTTP_R.url.substr(temp).c_str() );
            #if DEBUG >= 4
            printf( "Quality: %s, Seg %d Frag %d\n", Quality.c_str(), Segment, ReqFragment);
            #endif
            std::stringstream sstream;
            sstream << "f " << ReqFragment << "\no \n";
            ss.Send(sstream.str().c_str());
            ss.flush();
            Flash_RequestPending++;
          }else{
            streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1);
            if (!Strm.metadata.isNull()){
              HTTP_S.Clean();
              HTTP_S.SetHeader("Content-Type","text/xml");
              HTTP_S.SetHeader("Cache-Control","no-cache");
              if (Strm.metadata.isMember("length")){receive_marks = true;}
              std::string manifest = BuildManifest(streamname, Strm.metadata);
              HTTP_S.SetBody(manifest);
              conn.Send(HTTP_S.BuildResponse("200", "OK"));
              #if DEBUG >= 3
              printf("Sent manifest\n");
              #endif
              pending_manifest = false;
            }else{
              pending_manifest = true;
            }
          }
          ready4data = true;
          HTTP_R.Clean(); //clean for any possible next requests
        }else{
          #if DEBUG >= 3
          fprintf(stderr, "Could not parse the following:\n%s\n", conn.Received().c_str());
          #endif
        }
      }
      if (ready4data){
        if (!inited){
          //we are ready, connect the socket!
          ss = Util::Stream::getStream(streamname);
          if (!ss.connected()){
            #if DEBUG >= 1
            fprintf(stderr, "Could not connect to server!\n");
            #endif
            ss.close();
            HTTP_S.Clean();
            HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
            conn.Send(HTTP_S.BuildResponse("404", "Not found"));
            ready4data = false;
            continue;
          }
          ss.setBlocking(false);
          #if DEBUG >= 3
          fprintf(stderr, "Everything connected, starting to send video data...\n");
          #endif
          inited = true;
        }
        if ((Flash_RequestPending > 0) && !Flash_FragBuffer.empty()){
          HTTP_S.Clean();
          HTTP_S.SetHeader("Content-Type","video/mp4");
          HTTP_S.SetBody(MP4::mdatFold(Flash_FragBuffer.front()));
          Flash_FragBuffer.pop();
          conn.Send(HTTP_S.BuildResponse("200", "OK"));
          Flash_RequestPending--;
          #if DEBUG >= 3
          fprintf(stderr, "Sending a video fragment. %i left in buffer, %i requested\n", (int)Flash_FragBuffer.size(), Flash_RequestPending);
          #endif
        }
        unsigned int now = time(0);
        if (now != lastStats){
          lastStats = now;
          ss.Send("S ");
          ss.Send(conn.getStats("HTTP_Dynamic").c_str());
        }
        if (ss.spool() || ss.Received() != ""){
          if (Strm.parsePacket(ss.Received())){
            if (Strm.getPacket(0).isMember("time")){
              if (!Strm.metadata.isMember("firsttime")){
                Strm.metadata["firsttime"] = Strm.getPacket(0)["time"];
              }else{
                if (!Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){
                  Strm.getPacket(0)["time"] = Strm.getPacket(0)["time"].asInt() - Strm.metadata["firsttime"].asInt();
                }
              }
              Strm.metadata["lasttime"] = Strm.getPacket(0)["time"];
            }
            if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
              tag.DTSCLoader(Strm);
            }
            if (pending_manifest){
              HTTP_S.Clean();
              HTTP_S.SetHeader("Content-Type","text/xml");
              HTTP_S.SetHeader("Cache-Control","no-cache");
              if (Strm.metadata.isMember("length")){receive_marks = true;}
              std::string manifest = BuildManifest(streamname, Strm.metadata);
              HTTP_S.SetBody(manifest);
              conn.Send(HTTP_S.BuildResponse("200", "OK"));
              #if DEBUG >= 3
              printf("Sent manifest\n");
              #endif
              pending_manifest = false;
            }
            if (!receive_marks && Strm.metadata.isMember("length")){receive_marks = true;}
            if ((Strm.getPacket(0).isMember("keyframe") && !receive_marks) || Strm.lastType() == DTSC::PAUSEMARK){
              if (flashbuf_nonempty || Strm.lastType() == DTSC::PAUSEMARK){
                Flash_FragBuffer.push(FlashBuf);
                while (Flash_FragBuffer.size() > 2){
                  Flash_FragBuffer.pop();
                }
                #if DEBUG >= 4
                fprintf(stderr, "Received a %s fragment of %i packets. Now %i in buffer.\n", Strm.getPacket(0)["datatype"].asString().c_str(), flashbuf_nonempty, (int)Flash_FragBuffer.size());
                #endif
              }
              FlashBuf.clear();
              flashbuf_nonempty = 0;
              //fill buffer with init data, if needed.
              if (Strm.metadata.isMember("audio") && Strm.metadata["audio"].isMember("init")){
                tmp.DTSCAudioInit(Strm);
                FlashBuf.append(tmp.data, tmp.len);
              }
              if (Strm.metadata.isMember("video") && Strm.metadata["video"].isMember("init")){
                tmp.DTSCVideoInit(Strm);
                FlashBuf.append(tmp.data, tmp.len);
              }
            }
            if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
              ++flashbuf_nonempty;
              FlashBuf.append(tag.data, tag.len);
            }
          }else{
            if (pending_manifest && !Strm.metadata.isNull()){
              HTTP_S.Clean();
              HTTP_S.SetHeader("Content-Type","text/xml");
              HTTP_S.SetHeader("Cache-Control","no-cache");
              if (Strm.metadata.isMember("length")){receive_marks = true;}
              std::string manifest = BuildManifest(streamname, Strm.metadata);
              HTTP_S.SetBody(manifest);
              conn.Send(HTTP_S.BuildResponse("200", "OK"));
              #if DEBUG >= 3
              printf("Sent manifest\n");
              #endif
              pending_manifest = false;
            }
          }
        }
        if (!ss.connected()){break;}
      }
    }
    conn.close();
    ss.Send("S ");
    ss.Send(conn.getStats("HTTP_Dynamic").c_str());
    ss.flush();
    ss.close();
    #if DEBUG >= 1
    if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());}
    fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
    if (inited){
      fprintf(stderr, "Status was: inited\n");
    }else{
      if (ready4data){
        fprintf(stderr, "Status was: ready4data\n");
      }else{
        fprintf(stderr, "Status was: connected\n");
      }
    }
    #endif
    return 0;
  }//Connector_HTTP_Dynamic main function

};//Connector_HTTP_Dynamic namespace

int main(int argc, char ** argv){
  Util::Config conf(argv[0], PACKAGE_VERSION);
  conf.addConnectorOptions(1935);
  conf.parseArgs(argc, argv);
  Socket::Server server_socket = Socket::Server("/tmp/mist/http_dynamic");
  if (!server_socket.connected()){return 1;}
  conf.activate();

  while (server_socket.connected() && conf.is_active){
    Socket::Connection S = server_socket.accept();
    if (S.connected()){//check if the new connection is valid
      pid_t myid = fork();
      if (myid == 0){//if new child, start MAINHANDLER
        return Connector_HTTP::Connector_HTTP_Dynamic(S);
      }else{//otherwise, do nothing or output debugging text
        #if DEBUG >= 3
        fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
        #endif
      }
    }
  }//while connected
  server_socket.close();
  return 0;
}//main
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2021 Daniele Bartolini et al.
 * License: https://github.com/dbartolini/crown/blob/master/LICENSE
 */

#pragma once

#include "core/math/aabb.inl"
#include "core/math/intersection.h"
#include "core/math/plane3.inl"

namespace crown
{
/// Functions to manipulate Frustum.
///
/// @ingroup Math
namespace frustum
{
	/// Builds the frustum @a f from the view matrix @a m.
	void from_matrix(Frustum& f, const Matrix4x4& m);

	/// Returns whether the frustum @a f contains the point @a p.
	bool contains_point(const Frustum& f, const Vector3& p);

	/// Returns the @a vertices of the frustum @a f.
	/// @note
	/// 0 = Near bottom left
	/// 1 = Near bottom right
	/// 2 = Near top right
	/// 3 = Near top left
	/// 4 = Far bottom left
	/// 5 = Far bottom right
	/// 6 = Far top right
	/// 7 = Far top left
	void vertices(Vector3 vertices[8], const Frustum& f);

	/// Returns the AABB enclosing the frustum @a f.
	AABB to_aabb(const Frustum& f);

} // namespace frustum

namespace frustum
{
	inline void from_matrix(Frustum& f, const Matrix4x4& m)
	{
		f.planes[0].n.x = m.x.w + m.x.y;
		f.planes[0].n.y = m.y.w + m.y.y;
		f.planes[0].n.z = m.z.w + m.z.y;
		f.planes[0].d   = m.t.w + m.t.y;
		plane3::normalize(f.planes[0]);

		f.planes[1].n.x = m.x.w - m.x.x;
		f.planes[1].n.y = m.y.w - m.y.x;
		f.planes[1].n.z = m.z.w - m.z.x;
		f.planes[1].d   = m.t.w - m.t.x;
		plane3::normalize(f.planes[1]);

		f.planes[2].n.x = m.x.w - m.x.y;
		f.planes[2].n.y = m.y.w - m.y.y;
		f.planes[2].n.z = m.z.w - m.z.y;
		f.planes[2].d   = m.t.w - m.t.y;
		plane3::normalize(f.planes[2]);

		f.planes[3].n.x = m.x.w + m.x.x;
		f.planes[3].n.y = m.y.w + m.y.x;
		f.planes[3].n.z = m.z.w + m.z.x;
		f.planes[3].d   = m.t.w + m.t.x;
		plane3::normalize(f.planes[3]);

		f.planes[4].n.x = m.x.z;
		f.planes[4].n.y = m.y.z;
		f.planes[4].n.z = m.z.z;
		f.planes[4].d   = m.t.z;
		plane3::normalize(f.planes[4]);

		f.planes[5].n.x = m.x.w - m.x.z;
		f.planes[5].n.y = m.y.w - m.y.z;
		f.planes[5].n.z = m.z.w - m.z.z;
		f.planes[5].d   = m.t.w - m.t.z;
		plane3::normalize(f.planes[5]);
	}

	inline bool contains_point(const Frustum& f, const Vector3& p)
	{
		return !(plane3::distance_to_point(f.planes[3], p) < 0.0f
			|| plane3::distance_to_point(f.planes[1], p) < 0.0f
			|| plane3::distance_to_point(f.planes[0], p) < 0.0f
			|| plane3::distance_to_point(f.planes[2], p) < 0.0f
			|| plane3::distance_to_point(f.planes[4], p) < 0.0f
			|| plane3::distance_to_point(f.planes[5], p) < 0.0f
			);
	}

	inline void vertices(Vector3 vertices[8], const Frustum& f)
	{
		// p3 ---- p2  Front face.
		//  |      |
		//  |      |
		// p0 ---- p1
		plane_3_intersection(vertices[0], f.planes[4], f.planes[0], f.planes[3]);
		plane_3_intersection(vertices[1], f.planes[4], f.planes[0], f.planes[1]);
		plane_3_intersection(vertices[2], f.planes[4], f.planes[2], f.planes[1]);
		plane_3_intersection(vertices[3], f.planes[4], f.planes[2], f.planes[3]);

		// p7 ---- p6  Back face.
		//  |      |
		//  |      |
		// p4 ---- p5
		plane_3_intersection(vertices[4], f.planes[5], f.planes[0], f.planes[3]);
		plane_3_intersection(vertices[5], f.planes[5], f.planes[0], f.planes[1]);
		plane_3_intersection(vertices[6], f.planes[5], f.planes[2], f.planes[1]);
		plane_3_intersection(vertices[7], f.planes[5], f.planes[2], f.planes[3]);
	}

	inline AABB to_aabb(const Frustum& f)
	{
		Vector3 vertices[8];
		frustum::vertices(vertices, f);

		AABB r;
		aabb::from_points(r, countof(vertices), vertices);
		return r;
	}

} // namespace frustum

} // namespace crown
<commit_msg>core: fix frustum::from_matrix()<commit_after>/*
 * Copyright (c) 2012-2021 Daniele Bartolini et al.
 * License: https://github.com/dbartolini/crown/blob/master/LICENSE
 */

#pragma once

#include "core/math/aabb.inl"
#include "core/math/intersection.h"
#include "core/math/plane3.inl"

namespace crown
{
/// Functions to manipulate Frustum.
///
/// @ingroup Math
namespace frustum
{
	/// Builds the frustum @a f from the view matrix @a m.
	void from_matrix(Frustum& f, const Matrix4x4& m);

	/// Returns whether the frustum @a f contains the point @a p.
	bool contains_point(const Frustum& f, const Vector3& p);

	/// Returns the @a vertices of the frustum @a f.
	/// @note
	/// 0 = Near bottom left
	/// 1 = Near bottom right
	/// 2 = Near top right
	/// 3 = Near top left
	/// 4 = Far bottom left
	/// 5 = Far bottom right
	/// 6 = Far top right
	/// 7 = Far top left
	void vertices(Vector3 vertices[8], const Frustum& f);

	/// Returns the AABB enclosing the frustum @a f.
	AABB to_aabb(const Frustum& f);

} // namespace frustum

namespace frustum
{
	inline void from_matrix(Frustum& f, const Matrix4x4& m)
	{
		f.planes[0].n.x = m.x.w + m.x.y;
		f.planes[0].n.y = m.y.w + m.y.y;
		f.planes[0].n.z = m.z.w + m.z.y;
		f.planes[0].d = -(m.t.w + m.t.y);
		plane3::normalize(f.planes[0]);

		f.planes[1].n.x = m.x.w - m.x.x;
		f.planes[1].n.y = m.y.w - m.y.x;
		f.planes[1].n.z = m.z.w - m.z.x;
		f.planes[1].d = -(m.t.w - m.t.x);
		plane3::normalize(f.planes[1]);

		f.planes[2].n.x = m.x.w - m.x.y;
		f.planes[2].n.y = m.y.w - m.y.y;
		f.planes[2].n.z = m.z.w - m.z.y;
		f.planes[2].d = -(m.t.w - m.t.y);
		plane3::normalize(f.planes[2]);

		f.planes[3].n.x = m.x.w + m.x.x;
		f.planes[3].n.y = m.y.w + m.y.x;
		f.planes[3].n.z = m.z.w + m.z.x;
		f.planes[3].d = -(m.t.w + m.t.x);
		plane3::normalize(f.planes[3]);

		f.planes[4].n.x = m.x.z;
		f.planes[4].n.y = m.y.z;
		f.planes[4].n.z = m.z.z;
		f.planes[4].d = -(m.t.z);
		plane3::normalize(f.planes[4]);

		f.planes[5].n.x = m.x.w - m.x.z;
		f.planes[5].n.y = m.y.w - m.y.z;
		f.planes[5].n.z = m.z.w - m.z.z;
		f.planes[5].d = -(m.t.w - m.t.z);
		plane3::normalize(f.planes[5]);
	}

	inline bool contains_point(const Frustum& f, const Vector3& p)
	{
		return !(plane3::distance_to_point(f.planes[3], p) < 0.0f
			|| plane3::distance_to_point(f.planes[1], p) < 0.0f
			|| plane3::distance_to_point(f.planes[0], p) < 0.0f
			|| plane3::distance_to_point(f.planes[2], p) < 0.0f
			|| plane3::distance_to_point(f.planes[4], p) < 0.0f
			|| plane3::distance_to_point(f.planes[5], p) < 0.0f
			);
	}

	inline void vertices(Vector3 vertices[8], const Frustum& f)
	{
		// p3 ---- p2  Front face.
		//  |      |
		//  |      |
		// p0 ---- p1
		plane_3_intersection(vertices[0], f.planes[4], f.planes[0], f.planes[3]);
		plane_3_intersection(vertices[1], f.planes[4], f.planes[0], f.planes[1]);
		plane_3_intersection(vertices[2], f.planes[4], f.planes[2], f.planes[1]);
		plane_3_intersection(vertices[3], f.planes[4], f.planes[2], f.planes[3]);

		// p7 ---- p6  Back face.
		//  |      |
		//  |      |
		// p4 ---- p5
		plane_3_intersection(vertices[4], f.planes[5], f.planes[0], f.planes[3]);
		plane_3_intersection(vertices[5], f.planes[5], f.planes[0], f.planes[1]);
		plane_3_intersection(vertices[6], f.planes[5], f.planes[2], f.planes[1]);
		plane_3_intersection(vertices[7], f.planes[5], f.planes[2], f.planes[3]);
	}

	inline AABB to_aabb(const Frustum& f)
	{
		Vector3 vertices[8];
		frustum::vertices(vertices, f);

		AABB r;
		aabb::from_points(r, countof(vertices), vertices);
		return r;
	}

} // namespace frustum

} // namespace crown
<|endoftext|>


<commit_before>/* This software is released under the BSD License.
 |
 | Copyright (c) 2015, Kevin P. Barry [ta0kira@gmail.com]
 | All rights reserved.
 |
 | Redistribution  and  use  in  source  and   binary  forms,  with  or  without
 | modification, are permitted provided that the following conditions are met:
 |
 | - Redistributions of source code must retain the above copyright notice, this
 |   list of conditions and the following disclaimer.
 |
 | - Redistributions in binary  form must reproduce the  above copyright notice,
 |   this list  of conditions and the following disclaimer in  the documentation
 |   and/or other materials provided with the distribution.
 |
 | - Neither the name  of the  Locking Container Project  nor  the names  of its
 |   contributors may be  used to endorse or promote products  derived from this
 |   software without specific prior written permission.
 |
 | THIS SOFTWARE IS  PROVIDED BY THE COPYRIGHT HOLDERS AND  CONTRIBUTORS "AS IS"
 | AND ANY  EXPRESS OR IMPLIED  WARRANTIES,  INCLUDING, BUT  NOT LIMITED TO, THE
 | IMPLIED WARRANTIES OF  MERCHANTABILITY  AND FITNESS FOR A  PARTICULAR PURPOSE
 | ARE DISCLAIMED.  IN  NO EVENT SHALL  THE COPYRIGHT  OWNER  OR CONTRIBUTORS BE
 | LIABLE  FOR  ANY  DIRECT,   INDIRECT,  INCIDENTAL,   SPECIAL,  EXEMPLARY,  OR
 | CONSEQUENTIAL   DAMAGES  (INCLUDING,  BUT  NOT  LIMITED  TO,  PROCUREMENT  OF
 | SUBSTITUTE GOODS OR SERVICES;  LOSS  OF USE,  DATA,  OR PROFITS;  OR BUSINESS
 | INTERRUPTION)  HOWEVER  CAUSED  AND ON  ANY  THEORY OF LIABILITY,  WHETHER IN
 | CONTRACT,  STRICT  LIABILITY, OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
 | ARISING IN ANY  WAY OUT OF  THE USE OF THIS SOFTWARE, EVEN  IF ADVISED OF THE
 | POSSIBILITY OF SUCH DAMAGE.
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

#ifndef locks_hpp
#define locks_hpp

#include <atomic>
#include <memory>

#include <assert.h>
#include <pthread.h>

#include "lock-auth.hpp"

/*! \class lock_base
    \brief Base class for lock classes.
 */

class lock_base {
public:
  typedef lock_auth_base::count_type count_type;

  /*! Return < 0 must mean failure. Should return the current number of read locks on success.*/
  virtual count_type lock(lock_auth_base *auth, bool read, bool block = true, bool test = false) = 0;

  /*! Return < 0 must mean failure. Should return the current number of read locks on success.*/
  virtual count_type unlock(lock_auth_base *auth, bool read, bool test = false) = 0;

protected:
  static inline bool register_or_test_auth(lock_auth_base *auth, bool read, bool lock_out,
    bool in_use, bool test_auth) {
    if (!auth) return true;
    return test_auth? auth->register_auth(read, lock_out, in_use) :
                      auth->test_auth(read, lock_out, in_use);
  }

  static inline void release_auth(lock_auth_base *auth, bool read) {
    if (auth) auth->release_auth(read);
  }
};

/*! \class rw_lock
    \brief Lock object that allows multiple readers at once.
 *
 * This lock allows multiple readers at a time. This is the default lock used.
 * A write lock can only be obtained if no other readers or writers have a lock.
 * If a thread attempts to obtain a write lock and there are readers, it will
 * block until all readers leave, blocking out all new readers and writers in
 * the meantime. If 'lock_auth <rw_lock>' authorization is used (see below), the
 * holder of the write lock can subsequently obtain a new read lock for the same
 * container; otherwise, all read locks will be denied while the write lock is
 * in place.
 */

class rw_lock : public lock_base {
public:
  using lock_base::count_type;

  rw_lock() : readers(0), readers_waiting(0), writer(false), writer_waiting(false), the_writer(NULL) {
    pthread_mutex_init(&master_lock, NULL);
    pthread_cond_init(&read_wait, NULL);
    pthread_cond_init(&write_wait, NULL);
  }

private:
  rw_lock(const rw_lock&);
  rw_lock &operator = (const rw_lock&);

public:
  long lock(lock_auth_base *auth, bool read, bool block = true, bool test = false) {
    if (pthread_mutex_lock(&master_lock) != 0) return -1;
    bool writer_reads = auth && the_writer == auth && read;
    //make sure this is an authorized lock type for the caller
    if (!register_or_test_auth(auth, read, writer_reads? false : writer_waiting,
                               writer_reads? false : (writer || readers), test)) {
      pthread_mutex_unlock(&master_lock);
      return -1;
    }
    //check for blocking behavior
    bool must_block = writer || writer_waiting || (!read && readers);
    //exception to blocking: if 'auth' holds the write lock and a read is requested
    if (!writer_reads && !block && must_block) {
      if (!test) release_auth(auth, read);
      pthread_mutex_unlock(&master_lock);
      return -1;
    }
    if (read) {
      //get a read lock
      ++readers_waiting;
      //NOTE: 'auth' is expected to prevent a deadlock if the caller already has
      //a read lock and there is a writer waiting
      if (!writer_reads) while (writer || writer_waiting) {
        if (pthread_cond_wait(&read_wait, &master_lock) != 0) {
          if (!test) release_auth(auth, read);
          --readers_waiting;
          pthread_mutex_unlock(&master_lock);
          return -1;
        }
      }
      --readers_waiting;
      count_type new_readers = ++readers;
      //if for some strange reason there's an overflow...
      assert((writer_reads || (!writer && !writer_waiting)) && readers > 0);
      pthread_mutex_unlock(&master_lock);
      return new_readers;
    } else {
      //if the caller isn't the first in line for writing, wait until it is
      ++readers_waiting;
      while (writer_waiting) {
        //NOTE: use 'read_wait' here, since that's what a write unlock broadcasts on
        //NOTE: another thread should be blocking in 'write_wait' below
        if (pthread_cond_wait(&read_wait, &master_lock) != 0) {
          if (!test) release_auth(auth, read);
          --readers_waiting;
          pthread_mutex_unlock(&master_lock);
          return -1;
        }
      }
      --readers_waiting;
      writer_waiting = true;
      //get a write lock
      while (writer || readers) {
        if (pthread_cond_wait(&write_wait, &master_lock) != 0) {
          if (!test) release_auth(auth, read);
          writer_waiting = false;
          pthread_mutex_unlock(&master_lock);
          return -1;
        }
      }
      writer_waiting = false;
      writer = true;
      the_writer = auth;
      pthread_mutex_unlock(&master_lock);
      return 0;
    }
  }

  count_type unlock(lock_auth_base *auth, bool read, bool test = false) {
    if (pthread_mutex_lock(&master_lock) != 0) return -1;
    if (!test) release_auth(auth, read);
    if (read) {
      assert(((auth && the_writer == auth) || !writer) && readers > 0);
      count_type new_readers = --readers;
      if (!new_readers && writer_waiting) {
        pthread_cond_broadcast(&write_wait);
      }
      pthread_mutex_unlock(&master_lock);
      return new_readers;
    } else {
      assert(writer && ((auth && the_writer == auth) || !readers));
      assert(the_writer == auth);
      writer = false;
      the_writer = NULL;
      if (writer_waiting) {
        pthread_cond_broadcast(&write_wait);
      }
      if (readers_waiting) {
        pthread_cond_broadcast(&read_wait);
      }
      pthread_mutex_unlock(&master_lock);
      return 0;
    }
  }

  ~rw_lock() {
    assert(!readers && !readers_waiting && !writer && !writer_waiting);
    pthread_mutex_destroy(&master_lock);
    pthread_cond_destroy(&read_wait);
    pthread_cond_destroy(&write_wait);
  }

private:
  count_type       readers, readers_waiting;
  bool             writer, writer_waiting;
  const void      *the_writer;
  pthread_mutex_t  master_lock;
  pthread_cond_t   read_wait, write_wait;
};

/*! \class r_lock
    \brief Lock object that allows multiple readers but no writers.
 *
 * This lock allows multiple readers, but it never allows writers. This might be
 * useful if you have a container that will never be written to but you
 * nevertheless need to retain the same container semantics.
 */

class r_lock : public lock_base {
public:
  using lock_base::count_type;

  r_lock() : readers(0) {}

private:
  r_lock(const r_lock&);
  r_lock &operator = (const r_lock&);

public:
  count_type lock(lock_auth_base *auth, bool read, bool /*block*/ = true, bool test = false) {
    if (!read) return -1;
    if (!register_or_test_auth(auth, read, false, false, test)) return -1;
    //NOTE: this is atomic
    count_type new_readers = ++readers;
    //(check the copy!)
    assert(new_readers > 0);
    return new_readers;
  }

  count_type unlock(lock_auth_base *auth, bool read, bool test = false) {
    if (!read) return -1;
    if (!test) release_auth(auth, read);
    //NOTE: this is atomic
    count_type new_readers = --readers;
    //(check the copy!)
    assert(new_readers >= 0);
    return new_readers;
  }

  ~r_lock() {
    assert(!readers);
  }

private:
  std::atomic <count_type> readers;
};

/*! \class w_lock
    \brief Lock object that allows only one thread access at a time.
 *
 * This lock doesn't make a distinction between readers and writers; only one
 * thread can hold a lock at any given time. This should operate faster if you
 * don't need read locks. Note that, for the purposes of deadlock prevention,
 * this treats all locks as write locks.
 */

class w_lock : public lock_base {
public:
  using lock_base::count_type;

  w_lock() : locked(false) {
    pthread_mutex_init(&write_lock, NULL);
  }

private:
  w_lock(const w_lock&);
  w_lock &operator = (const w_lock&);

public:
  count_type lock(lock_auth_base *auth, bool /*read*/, bool block = true, bool test = false) {
    //NOTE: 'false' is passed instead of 'read' because this can lock out other readers
    if (!register_or_test_auth(auth, false, locked, locked, test)) return -1;
    if ((block? pthread_mutex_lock : pthread_mutex_trylock)(&write_lock) != 0) {
      if (!test) release_auth(auth, false);
      return -1;
    }
    assert(!locked);
    locked = true;
    return 0;
  }

  count_type unlock(lock_auth_base *auth, bool /*read*/, bool test = false) {
    if (!test) release_auth(auth, false);
    assert(locked);
    locked = false;
    return (pthread_mutex_unlock(&write_lock) == 0)? 0 : -1;
  }

  ~w_lock() {
    assert(!locked);
    pthread_mutex_destroy(&write_lock);
  }

private:
  bool            locked;
  pthread_mutex_t write_lock;
};

/*! \class broken_lock
    \brief Lock object that is permanently broken.
 *
 * This is mostly a joke; however, you can use it to test pathological cases.
 * This lock will always fail to lock and unlock.
 */

struct broken_lock : public lock_base {
  using lock_base::count_type;

  count_type lock(lock_auth_base* /*auth*/, bool /*read*/, bool /*block*/ = true,
    bool /*test*/ = false) { return -1; }
  count_type unlock(lock_auth_base* /*auth*/, bool /*read*/, bool /*test*/ = false) { return -1; }
};

#endif //locks_hpp
<commit_msg>fixed a bug with register vs. test lock<commit_after>/* This software is released under the BSD License.
 |
 | Copyright (c) 2015, Kevin P. Barry [ta0kira@gmail.com]
 | All rights reserved.
 |
 | Redistribution  and  use  in  source  and   binary  forms,  with  or  without
 | modification, are permitted provided that the following conditions are met:
 |
 | - Redistributions of source code must retain the above copyright notice, this
 |   list of conditions and the following disclaimer.
 |
 | - Redistributions in binary  form must reproduce the  above copyright notice,
 |   this list  of conditions and the following disclaimer in  the documentation
 |   and/or other materials provided with the distribution.
 |
 | - Neither the name  of the  Locking Container Project  nor  the names  of its
 |   contributors may be  used to endorse or promote products  derived from this
 |   software without specific prior written permission.
 |
 | THIS SOFTWARE IS  PROVIDED BY THE COPYRIGHT HOLDERS AND  CONTRIBUTORS "AS IS"
 | AND ANY  EXPRESS OR IMPLIED  WARRANTIES,  INCLUDING, BUT  NOT LIMITED TO, THE
 | IMPLIED WARRANTIES OF  MERCHANTABILITY  AND FITNESS FOR A  PARTICULAR PURPOSE
 | ARE DISCLAIMED.  IN  NO EVENT SHALL  THE COPYRIGHT  OWNER  OR CONTRIBUTORS BE
 | LIABLE  FOR  ANY  DIRECT,   INDIRECT,  INCIDENTAL,   SPECIAL,  EXEMPLARY,  OR
 | CONSEQUENTIAL   DAMAGES  (INCLUDING,  BUT  NOT  LIMITED  TO,  PROCUREMENT  OF
 | SUBSTITUTE GOODS OR SERVICES;  LOSS  OF USE,  DATA,  OR PROFITS;  OR BUSINESS
 | INTERRUPTION)  HOWEVER  CAUSED  AND ON  ANY  THEORY OF LIABILITY,  WHETHER IN
 | CONTRACT,  STRICT  LIABILITY, OR  TORT (INCLUDING  NEGLIGENCE  OR  OTHERWISE)
 | ARISING IN ANY  WAY OUT OF  THE USE OF THIS SOFTWARE, EVEN  IF ADVISED OF THE
 | POSSIBILITY OF SUCH DAMAGE.
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

#ifndef locks_hpp
#define locks_hpp

#include <atomic>
#include <memory>

#include <assert.h>
#include <pthread.h>

#include "lock-auth.hpp"

/*! \class lock_base
    \brief Base class for lock classes.
 */

class lock_base {
public:
  typedef lock_auth_base::count_type count_type;

  /*! Return < 0 must mean failure. Should return the current number of read locks on success.*/
  virtual count_type lock(lock_auth_base *auth, bool read, bool block = true, bool test = false) = 0;

  /*! Return < 0 must mean failure. Should return the current number of read locks on success.*/
  virtual count_type unlock(lock_auth_base *auth, bool read, bool test = false) = 0;

protected:
  static inline bool register_or_test_auth(lock_auth_base *auth, bool read, bool lock_out,
    bool in_use, bool test_auth) {
    if (!auth) return true;
    return test_auth? auth->test_auth(read, lock_out, in_use) :
                      auth->register_auth(read, lock_out, in_use);
  }

  static inline void release_auth(lock_auth_base *auth, bool read) {
    if (auth) auth->release_auth(read);
  }
};

/*! \class rw_lock
    \brief Lock object that allows multiple readers at once.
 *
 * This lock allows multiple readers at a time. This is the default lock used.
 * A write lock can only be obtained if no other readers or writers have a lock.
 * If a thread attempts to obtain a write lock and there are readers, it will
 * block until all readers leave, blocking out all new readers and writers in
 * the meantime. If 'lock_auth <rw_lock>' authorization is used (see below), the
 * holder of the write lock can subsequently obtain a new read lock for the same
 * container; otherwise, all read locks will be denied while the write lock is
 * in place.
 */

class rw_lock : public lock_base {
public:
  using lock_base::count_type;

  rw_lock() : readers(0), readers_waiting(0), writer(false), writer_waiting(false), the_writer(NULL) {
    pthread_mutex_init(&master_lock, NULL);
    pthread_cond_init(&read_wait, NULL);
    pthread_cond_init(&write_wait, NULL);
  }

private:
  rw_lock(const rw_lock&);
  rw_lock &operator = (const rw_lock&);

public:
  long lock(lock_auth_base *auth, bool read, bool block = true, bool test = false) {
    if (pthread_mutex_lock(&master_lock) != 0) return -1;
    bool writer_reads = auth && the_writer == auth && read;
    //make sure this is an authorized lock type for the caller
    if (!register_or_test_auth(auth, read, writer_reads? false : writer_waiting,
                               writer_reads? false : (writer || readers), test)) {
      pthread_mutex_unlock(&master_lock);
      return -1;
    }
    //check for blocking behavior
    bool must_block = writer || writer_waiting || (!read && readers);
    //exception to blocking: if 'auth' holds the write lock and a read is requested
    if (!writer_reads && !block && must_block) {
      if (!test) release_auth(auth, read);
      pthread_mutex_unlock(&master_lock);
      return -1;
    }
    if (read) {
      //get a read lock
      ++readers_waiting;
      //NOTE: 'auth' is expected to prevent a deadlock if the caller already has
      //a read lock and there is a writer waiting
      if (!writer_reads) while (writer || writer_waiting) {
        if (pthread_cond_wait(&read_wait, &master_lock) != 0) {
          if (!test) release_auth(auth, read);
          --readers_waiting;
          pthread_mutex_unlock(&master_lock);
          return -1;
        }
      }
      --readers_waiting;
      count_type new_readers = ++readers;
      //if for some strange reason there's an overflow...
      assert((writer_reads || (!writer && !writer_waiting)) && readers > 0);
      pthread_mutex_unlock(&master_lock);
      return new_readers;
    } else {
      //if the caller isn't the first in line for writing, wait until it is
      ++readers_waiting;
      while (writer_waiting) {
        //NOTE: use 'read_wait' here, since that's what a write unlock broadcasts on
        //NOTE: another thread should be blocking in 'write_wait' below
        if (pthread_cond_wait(&read_wait, &master_lock) != 0) {
          if (!test) release_auth(auth, read);
          --readers_waiting;
          pthread_mutex_unlock(&master_lock);
          return -1;
        }
      }
      --readers_waiting;
      writer_waiting = true;
      //get a write lock
      while (writer || readers) {
        if (pthread_cond_wait(&write_wait, &master_lock) != 0) {
          if (!test) release_auth(auth, read);
          writer_waiting = false;
          pthread_mutex_unlock(&master_lock);
          return -1;
        }
      }
      writer_waiting = false;
      writer = true;
      the_writer = auth;
      pthread_mutex_unlock(&master_lock);
      return 0;
    }
  }

  count_type unlock(lock_auth_base *auth, bool read, bool test = false) {
    if (pthread_mutex_lock(&master_lock) != 0) return -1;
    if (!test) release_auth(auth, read);
    if (read) {
      assert(((auth && the_writer == auth) || !writer) && readers > 0);
      count_type new_readers = --readers;
      if (!new_readers && writer_waiting) {
        pthread_cond_broadcast(&write_wait);
      }
      pthread_mutex_unlock(&master_lock);
      return new_readers;
    } else {
      assert(writer && ((auth && the_writer == auth) || !readers));
      assert(the_writer == auth);
      writer = false;
      the_writer = NULL;
      if (writer_waiting) {
        pthread_cond_broadcast(&write_wait);
      }
      if (readers_waiting) {
        pthread_cond_broadcast(&read_wait);
      }
      pthread_mutex_unlock(&master_lock);
      return 0;
    }
  }

  ~rw_lock() {
    assert(!readers && !readers_waiting && !writer && !writer_waiting);
    pthread_mutex_destroy(&master_lock);
    pthread_cond_destroy(&read_wait);
    pthread_cond_destroy(&write_wait);
  }

private:
  count_type       readers, readers_waiting;
  bool             writer, writer_waiting;
  const void      *the_writer;
  pthread_mutex_t  master_lock;
  pthread_cond_t   read_wait, write_wait;
};

/*! \class r_lock
    \brief Lock object that allows multiple readers but no writers.
 *
 * This lock allows multiple readers, but it never allows writers. This might be
 * useful if you have a container that will never be written to but you
 * nevertheless need to retain the same container semantics.
 */

class r_lock : public lock_base {
public:
  using lock_base::count_type;

  r_lock() : readers(0) {}

private:
  r_lock(const r_lock&);
  r_lock &operator = (const r_lock&);

public:
  count_type lock(lock_auth_base *auth, bool read, bool /*block*/ = true, bool test = false) {
    if (!read) return -1;
    if (!register_or_test_auth(auth, read, false, false, test)) return -1;
    //NOTE: this is atomic
    count_type new_readers = ++readers;
    //(check the copy!)
    assert(new_readers > 0);
    return new_readers;
  }

  count_type unlock(lock_auth_base *auth, bool read, bool test = false) {
    if (!read) return -1;
    if (!test) release_auth(auth, read);
    //NOTE: this is atomic
    count_type new_readers = --readers;
    //(check the copy!)
    assert(new_readers >= 0);
    return new_readers;
  }

  ~r_lock() {
    assert(!readers);
  }

private:
  std::atomic <count_type> readers;
};

/*! \class w_lock
    \brief Lock object that allows only one thread access at a time.
 *
 * This lock doesn't make a distinction between readers and writers; only one
 * thread can hold a lock at any given time. This should operate faster if you
 * don't need read locks. Note that, for the purposes of deadlock prevention,
 * this treats all locks as write locks.
 */

class w_lock : public lock_base {
public:
  using lock_base::count_type;

  w_lock() : locked(false) {
    pthread_mutex_init(&write_lock, NULL);
  }

private:
  w_lock(const w_lock&);
  w_lock &operator = (const w_lock&);

public:
  count_type lock(lock_auth_base *auth, bool /*read*/, bool block = true, bool test = false) {
    //NOTE: 'false' is passed instead of 'read' because this can lock out other readers
    if (!register_or_test_auth(auth, false, locked, locked, test)) return -1;
    if ((block? pthread_mutex_lock : pthread_mutex_trylock)(&write_lock) != 0) {
      if (!test) release_auth(auth, false);
      return -1;
    }
    assert(!locked);
    locked = true;
    return 0;
  }

  count_type unlock(lock_auth_base *auth, bool /*read*/, bool test = false) {
    if (!test) release_auth(auth, false);
    assert(locked);
    locked = false;
    return (pthread_mutex_unlock(&write_lock) == 0)? 0 : -1;
  }

  ~w_lock() {
    assert(!locked);
    pthread_mutex_destroy(&write_lock);
  }

private:
  bool            locked;
  pthread_mutex_t write_lock;
};

/*! \class broken_lock
    \brief Lock object that is permanently broken.
 *
 * This is mostly a joke; however, you can use it to test pathological cases.
 * This lock will always fail to lock and unlock.
 */

struct broken_lock : public lock_base {
  using lock_base::count_type;

  count_type lock(lock_auth_base* /*auth*/, bool /*read*/, bool /*block*/ = true,
    bool /*test*/ = false) { return -1; }
  count_type unlock(lock_auth_base* /*auth*/, bool /*read*/, bool /*test*/ = false) { return -1; }
};

#endif //locks_hpp
<|endoftext|>


<commit_before>// @(#)root/graf:$Name:  $:$Id: TArc.cxx,v 1.9 2005/11/11 17:31:48 couet Exp $
// Author: Rene Brun   16/10/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "Riostream.h"
#include "TROOT.h"
#include "TArc.h"
#include "TVirtualPad.h"

ClassImp(TArc)

//______________________________________________________________________________
//
//  An arc is specified with the position of its centre, its radius
//  a minimum and maximum angle.
//  The attributes of the outline line are given via TAttLine
//  The attributes of the fill area are given via TAttFill
//

//______________________________________________________________________________
TArc::TArc(): TEllipse()
{
  // Arc  default constructor.
}

//______________________________________________________________________________
TArc::TArc(Double_t x1, Double_t y1,Double_t r1,Double_t phimin,Double_t phimax)
      :TEllipse(x1,y1,r1,r1,phimin,phimax,0)
{
   // Arc  normal constructor.
   //
   //  x1,y1  : coordinates of centre of arc
   //  r1     : arc radius
   //  phimin : min and max angle in degrees (default is 0-->360)
   //  phimax :
   //
   //  When a circle sector only is drawn, the lines connecting the center
   //  of the arc to the edges are drawn by default. One can specify
   //  the drawing option "only" to not draw these lines.
}

//______________________________________________________________________________
TArc::TArc(const TArc &arc) : TEllipse(arc)
{
   // Copy constructor.

   ((TArc&)arc).Copy(*this);
}

//______________________________________________________________________________
TArc::~TArc()
{
   // Arc default destructor.
}

//______________________________________________________________________________
void TArc::Copy(TObject &arc) const
{
   // Copy this arc to arc.

   TEllipse::Copy(arc);
}

//______________________________________________________________________________
void TArc::DrawArc(Double_t x1, Double_t y1,Double_t r1,Double_t phimin,Double_t phimax,Option_t *option)
{
   // Draw this arc with new coordinates.

   TArc *newarc = new TArc(x1, y1, r1, phimin, phimax);
   TAttLine::Copy(*newarc);
   TAttFill::Copy(*newarc);
   newarc->AppendPad(option);
}

//______________________________________________________________________________
void TArc::SavePrimitive(ostream &out, Option_t * /*= ""*/)
{
   // Save primitive as a C++ statement(s) on output stream out

   out<<"   "<<endl;
   if (gROOT->ClassSaved(TArc::Class())) {
      out<<"   ";
   } else {
      out<<"   TArc *";
   }
   out<<"arc = new TArc("<<fX1<<","<<fY1<<","<<fR1
      <<","<<fPhimin<<","<<fPhimax<<");"<<endl;

   SaveFillAttributes(out,"arc",0,1001);
   SaveLineAttributes(out,"arc",1,1,1);

   if (GetNoEdges()) out<<"   arc->SetNoEdges();"<<endl;

   out<<"   arc->Draw();"<<endl;
}
<commit_msg>In TArc::DrawArc, one must set the kCanDelete bit for the cloned object (thanks jan Musinski)<commit_after>// @(#)root/graf:$Name:  $:$Id: TArc.cxx,v 1.10 2006/07/03 16:10:44 brun Exp $
// Author: Rene Brun   16/10/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "Riostream.h"
#include "TROOT.h"
#include "TArc.h"
#include "TVirtualPad.h"

ClassImp(TArc)

//______________________________________________________________________________
//
//  An arc is specified with the position of its centre, its radius
//  a minimum and maximum angle.
//  The attributes of the outline line are given via TAttLine
//  The attributes of the fill area are given via TAttFill
//

//______________________________________________________________________________
TArc::TArc(): TEllipse()
{
  // Arc  default constructor.
}

//______________________________________________________________________________
TArc::TArc(Double_t x1, Double_t y1,Double_t r1,Double_t phimin,Double_t phimax)
      :TEllipse(x1,y1,r1,r1,phimin,phimax,0)
{
   // Arc  normal constructor.
   //
   //  x1,y1  : coordinates of centre of arc
   //  r1     : arc radius
   //  phimin : min and max angle in degrees (default is 0-->360)
   //  phimax :
   //
   //  When a circle sector only is drawn, the lines connecting the center
   //  of the arc to the edges are drawn by default. One can specify
   //  the drawing option "only" to not draw these lines.
}

//______________________________________________________________________________
TArc::TArc(const TArc &arc) : TEllipse(arc)
{
   // Copy constructor.

   ((TArc&)arc).Copy(*this);
}

//______________________________________________________________________________
TArc::~TArc()
{
   // Arc default destructor.
}

//______________________________________________________________________________
void TArc::Copy(TObject &arc) const
{
   // Copy this arc to arc.

   TEllipse::Copy(arc);
}

//______________________________________________________________________________
void TArc::DrawArc(Double_t x1, Double_t y1,Double_t r1,Double_t phimin,Double_t phimax,Option_t *option)
{
   // Draw this arc with new coordinates.

   TArc *newarc = new TArc(x1, y1, r1, phimin, phimax);
   TAttLine::Copy(*newarc);
   TAttFill::Copy(*newarc);
   newarc->SetBit(kCanDelete);
   newarc->AppendPad(option);
}

//______________________________________________________________________________
void TArc::SavePrimitive(ostream &out, Option_t * /*= ""*/)
{
   // Save primitive as a C++ statement(s) on output stream out

   out<<"   "<<endl;
   if (gROOT->ClassSaved(TArc::Class())) {
      out<<"   ";
   } else {
      out<<"   TArc *";
   }
   out<<"arc = new TArc("<<fX1<<","<<fY1<<","<<fR1
      <<","<<fPhimin<<","<<fPhimax<<");"<<endl;

   SaveFillAttributes(out,"arc",0,1001);
   SaveLineAttributes(out,"arc",1,1,1);

   if (GetNoEdges()) out<<"   arc->SetNoEdges();"<<endl;

   out<<"   arc->Draw();"<<endl;
}
<|endoftext|>


<commit_before>/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "QmitkSingleNodeSelectionWidget.h"

#include <berryQtStyleManager.h>
#include <QMouseEvent>

#include "QmitkNodeSelectionDialog.h"
#include "QmitkNodeDetailsDialog.h"

QmitkSingleNodeSelectionWidget::QmitkSingleNodeSelectionWidget(QWidget* parent) : QmitkAbstractNodeSelectionWidget(parent)
{
  m_Controls.setupUi(this);

  m_Controls.btnSelect->installEventFilter(this);
  m_Controls.btnSelect->setVisible(true);
  m_Controls.btnClear->setVisible(false);

  m_Controls.btnClear->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.common/times.svg")));

  this->UpdateInfo();

  connect(m_Controls.btnClear, SIGNAL(clicked(bool)), this, SLOT(OnClearSelection()));
}

QmitkSingleNodeSelectionWidget::~QmitkSingleNodeSelectionWidget()
{
}

mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::ExtractCurrentValidSelection(const NodeList& nodes) const
{
  mitk::DataNode::Pointer result = nullptr;

  for (auto node : nodes)
  {
    bool valid = true;
    if (m_NodePredicate.IsNotNull())
    {
      valid = m_NodePredicate->CheckNode(node);
    }
    if (valid)
    {
      result = node;
      break;
    }
  }

  return result;
}

QmitkSingleNodeSelectionWidget::NodeList QmitkSingleNodeSelectionWidget::CompileEmitSelection() const
{
  NodeList result;

  if (!m_SelectOnlyVisibleNodes)
  {
    result = m_ExternalSelection;
  }

  if (m_SelectedNode.IsNotNull() && !result.contains(m_SelectedNode))
  {
    result.append(m_SelectedNode);
  }

  return result;
}

void QmitkSingleNodeSelectionWidget::OnNodePredicateChanged(mitk::NodePredicateBase* /*newPredicate*/)
{
  m_SelectedNode = this->ExtractCurrentValidSelection(m_ExternalSelection);
};

void QmitkSingleNodeSelectionWidget::OnDataStorageChanged()
{
};

void QmitkSingleNodeSelectionWidget::OnClearSelection()
{
  if (m_IsOptional)
  {
    NodeList emptyList;
    this->SetCurrentSelection(emptyList);
  }

  this->UpdateInfo();
}

mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::GetSelectedNode() const
{
  return m_SelectedNode;
};

bool QmitkSingleNodeSelectionWidget::eventFilter(QObject *obj, QEvent *ev)
{
  if (obj == m_Controls.btnSelect)
  {
    if (ev->type() == QEvent::MouseButtonRelease)
    {
      auto mouseEv = dynamic_cast<QMouseEvent*>(ev);
      if (!mouseEv)
      {
        return false;
      }

      if (mouseEv->button() == Qt::LeftButton)
      {
        this->EditSelection();
        return true;
      }
      else
      {
        auto selection = this->CompileEmitSelection();
        if (!selection.empty())
        {
          QmitkNodeDetailsDialog infoDialog(selection, this);
          infoDialog.exec();
          return true;
        }
      }
    }
  }

  return false;
}

void QmitkSingleNodeSelectionWidget::EditSelection()
{
  QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this, m_PopUpTitel, m_PopUpHint);

  dialog->SetDataStorage(m_DataStorage.Lock());
  dialog->SetNodePredicate(m_NodePredicate);
  NodeList list;
  if (m_SelectedNode.IsNotNull())
  {
    list.append(m_SelectedNode);
  }
  dialog->SetCurrentSelection(list);
  dialog->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes);
  dialog->SetSelectionMode(QAbstractItemView::SingleSelection);

  m_Controls.btnSelect->setChecked(true);

  if (dialog->exec())
  {
    auto lastEmission = this->CompileEmitSelection();

    auto nodes = dialog->GetSelectedNodes();
    if (nodes.empty())
    {
      m_SelectedNode = nullptr;
    }
    else
    {
      m_SelectedNode = nodes.first();
    }

    auto newEmission = this->CompileEmitSelection();

    if (!EqualNodeSelections(lastEmission, newEmission))
    {
      emit CurrentSelectionChanged(newEmission);
      this->UpdateInfo();
    }
  }

  m_Controls.btnSelect->setChecked(false);

  delete dialog;
};

void QmitkSingleNodeSelectionWidget::UpdateInfo()
{
  if (m_SelectedNode.IsNull())
  {
    if (m_IsOptional)
    {
      m_Controls.btnSelect->SetNodeInfo(m_EmptyInfo);
    }
    else
    {
      m_Controls.btnSelect->SetNodeInfo(m_InvalidInfo);
    }
    m_Controls.btnClear->setVisible(false);
  }
  else
  {
    m_Controls.btnClear->setVisible(m_IsOptional);
  }

  m_Controls.btnSelect->SetSelectedNode(m_SelectedNode);
};

void QmitkSingleNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes)
{
  auto lastEmission = this->CompileEmitSelection();

  m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes;

  auto newEmission = this->CompileEmitSelection();

  if (!EqualNodeSelections(lastEmission, newEmission))
  {
    emit CurrentSelectionChanged(newEmission);
    this->UpdateInfo();
  }
};

void QmitkSingleNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes)
{
  auto lastEmission = this->CompileEmitSelection();

  m_ExternalSelection = selectedNodes;
  m_SelectedNode = this->ExtractCurrentValidSelection(selectedNodes);

  auto newEmission = this->CompileEmitSelection();

  if (!EqualNodeSelections(lastEmission, newEmission))
  {
    this->UpdateInfo();
    emit CurrentSelectionChanged(newEmission);
  }
};

void QmitkSingleNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node)
{
  if (m_SelectedNode == node && node != nullptr)
  {
    m_SelectedNode = nullptr;
    auto newEmission = this->CompileEmitSelection();

    emit CurrentSelectionChanged(newEmission);
    this->UpdateInfo();
  }
}
<commit_msg>Fixed SingleNodeSelectionWidget to not allow selection if disabled.<commit_after>/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "QmitkSingleNodeSelectionWidget.h"

#include <berryQtStyleManager.h>
#include <QMouseEvent>

#include "QmitkNodeSelectionDialog.h"
#include "QmitkNodeDetailsDialog.h"

QmitkSingleNodeSelectionWidget::QmitkSingleNodeSelectionWidget(QWidget* parent) : QmitkAbstractNodeSelectionWidget(parent)
{
  m_Controls.setupUi(this);

  m_Controls.btnSelect->installEventFilter(this);
  m_Controls.btnSelect->setVisible(true);
  m_Controls.btnClear->setVisible(false);

  m_Controls.btnClear->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.common/times.svg")));

  this->UpdateInfo();

  connect(m_Controls.btnClear, SIGNAL(clicked(bool)), this, SLOT(OnClearSelection()));
}

QmitkSingleNodeSelectionWidget::~QmitkSingleNodeSelectionWidget()
{
}

mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::ExtractCurrentValidSelection(const NodeList& nodes) const
{
  mitk::DataNode::Pointer result = nullptr;

  for (auto node : nodes)
  {
    bool valid = true;
    if (m_NodePredicate.IsNotNull())
    {
      valid = m_NodePredicate->CheckNode(node);
    }
    if (valid)
    {
      result = node;
      break;
    }
  }

  return result;
}

QmitkSingleNodeSelectionWidget::NodeList QmitkSingleNodeSelectionWidget::CompileEmitSelection() const
{
  NodeList result;

  if (!m_SelectOnlyVisibleNodes)
  {
    result = m_ExternalSelection;
  }

  if (m_SelectedNode.IsNotNull() && !result.contains(m_SelectedNode))
  {
    result.append(m_SelectedNode);
  }

  return result;
}

void QmitkSingleNodeSelectionWidget::OnNodePredicateChanged(mitk::NodePredicateBase* /*newPredicate*/)
{
  m_SelectedNode = this->ExtractCurrentValidSelection(m_ExternalSelection);
};

void QmitkSingleNodeSelectionWidget::OnDataStorageChanged()
{
};

void QmitkSingleNodeSelectionWidget::OnClearSelection()
{
  if (m_IsOptional)
  {
    NodeList emptyList;
    this->SetCurrentSelection(emptyList);
  }

  this->UpdateInfo();
}

mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::GetSelectedNode() const
{
  return m_SelectedNode;
};

bool QmitkSingleNodeSelectionWidget::eventFilter(QObject *obj, QEvent *ev)
{
  if (obj == m_Controls.btnSelect)
  {
    if (ev->type() == QEvent::MouseButtonRelease)
    {
      auto mouseEv = dynamic_cast<QMouseEvent*>(ev);
      if (!mouseEv)
      {
        return false;
      }

      if (mouseEv->button() == Qt::LeftButton && this->isEnabled())
      {
        this->EditSelection();
        return true;
      }
      else
      {
        auto selection = this->CompileEmitSelection();
        if (!selection.empty())
        {
          QmitkNodeDetailsDialog infoDialog(selection, this);
          infoDialog.exec();
          return true;
        }
      }
    }
  }

  return false;
}

void QmitkSingleNodeSelectionWidget::EditSelection()
{
  QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this, m_PopUpTitel, m_PopUpHint);

  dialog->SetDataStorage(m_DataStorage.Lock());
  dialog->SetNodePredicate(m_NodePredicate);
  NodeList list;
  if (m_SelectedNode.IsNotNull())
  {
    list.append(m_SelectedNode);
  }
  dialog->SetCurrentSelection(list);
  dialog->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes);
  dialog->SetSelectionMode(QAbstractItemView::SingleSelection);

  m_Controls.btnSelect->setChecked(true);

  if (dialog->exec())
  {
    auto lastEmission = this->CompileEmitSelection();

    auto nodes = dialog->GetSelectedNodes();
    if (nodes.empty())
    {
      m_SelectedNode = nullptr;
    }
    else
    {
      m_SelectedNode = nodes.first();
    }

    auto newEmission = this->CompileEmitSelection();

    if (!EqualNodeSelections(lastEmission, newEmission))
    {
      emit CurrentSelectionChanged(newEmission);
      this->UpdateInfo();
    }
  }

  m_Controls.btnSelect->setChecked(false);

  delete dialog;
};

void QmitkSingleNodeSelectionWidget::UpdateInfo()
{
  if (m_SelectedNode.IsNull())
  {
    if (m_IsOptional)
    {
      m_Controls.btnSelect->SetNodeInfo(m_EmptyInfo);
    }
    else
    {
      m_Controls.btnSelect->SetNodeInfo(m_InvalidInfo);
    }
    m_Controls.btnClear->setVisible(false);
  }
  else
  {
    m_Controls.btnClear->setVisible(m_IsOptional);
  }

  m_Controls.btnSelect->SetSelectedNode(m_SelectedNode);
};

void QmitkSingleNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes)
{
  auto lastEmission = this->CompileEmitSelection();

  m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes;

  auto newEmission = this->CompileEmitSelection();

  if (!EqualNodeSelections(lastEmission, newEmission))
  {
    emit CurrentSelectionChanged(newEmission);
    this->UpdateInfo();
  }
};

void QmitkSingleNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes)
{
  auto lastEmission = this->CompileEmitSelection();

  m_ExternalSelection = selectedNodes;
  m_SelectedNode = this->ExtractCurrentValidSelection(selectedNodes);

  auto newEmission = this->CompileEmitSelection();

  if (!EqualNodeSelections(lastEmission, newEmission))
  {
    this->UpdateInfo();
    emit CurrentSelectionChanged(newEmission);
  }
};

void QmitkSingleNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node)
{
  if (m_SelectedNode == node && node != nullptr)
  {
    m_SelectedNode = nullptr;
    auto newEmission = this->CompileEmitSelection();

    emit CurrentSelectionChanged(newEmission);
    this->UpdateInfo();
  }
}
<|endoftext|>


<commit_before><commit_msg>coverity#705828 Dereference before null check<commit_after><|endoftext|>


<commit_before>#include "boost_test.h"
#include "ComplexFixedPoint.h"
#include <complex>

using namespace std;

BOOST_AUTO_TEST_CASE( CFxpConstructors )
{
	/* Check constructors */
	CFxp b(1, -3, 4);
	BOOST_CHECK_EQUAL(b.width(), 4);
	BOOST_CHECK_EQUAL(b.real(), 1);
	BOOST_CHECK_EQUAL(b.imag(), -3);
	CFxp c(complex<int64_t>(5, -13), 6);
	BOOST_CHECK_EQUAL(c.width(), 6);
	BOOST_CHECK_EQUAL(c.real(), 5);
	BOOST_CHECK_EQUAL(c.imag(), -13);

	/* Values too large for width */
	BOOST_CHECK_THROW(CFxp(128, 0, 8), range_error);
	BOOST_CHECK_THROW(CFxp(0, -129, 8), range_error);

	/* Width larger than max allowed width */
	BOOST_CHECK_THROW(CFxp(0, 0, CFxp::MAX_WIDTH + 1), range_error);
}

BOOST_AUTO_TEST_CASE( CFxpAccessors )
{
	CFxp a(1, 2, 8);

	BOOST_CHECK_EQUAL(a.real(), 1);
	BOOST_CHECK_EQUAL(a.imag(), 2);
	BOOST_CHECK_EQUAL(a.width(), 8);
	BOOST_CHECK_EQUAL(a.minVal(), -128);
	BOOST_CHECK_EQUAL(a.maxVal(), 127);
}

BOOST_AUTO_TEST_CASE( CFxpAssignment )
{
	CFxp a(1, 2, 8);
	CFxp b(1, 2, 10);
	CFxp c(0, 0, 8);

	/* lhs and rhs widths must match by default */
	BOOST_CHECK_THROW(a = b, SizeMismatchException);

	/* Valid assignment */
	BOOST_CHECK_NO_THROW(c = a);
	BOOST_CHECK_EQUAL(c, a);
}

BOOST_AUTO_TEST_CASE( CFxpEquality )
{
	CFxp a(1, 2, 8);
	CFxp b(1, 2, 8);
	CFxp c(1, 2, 10);
	CFxp d(13, 2, 8);
	CFxp e(1, 13, 8);
	CFxp f(24, 38, 21);

	/* same object as lhs and rhs */
	BOOST_CHECK_EQUAL(a == a, true);
	BOOST_CHECK_EQUAL(a != a, false);

	/* different equal objects */
	BOOST_CHECK_EQUAL(a == b, true);
	BOOST_CHECK_EQUAL(a != b, false);

	/* same value, different widths */
	BOOST_CHECK_EQUAL(a == c, false);
	BOOST_CHECK_EQUAL(a != c, true);

	/* different values, same widths */
	BOOST_CHECK_EQUAL(a == d, false);
	BOOST_CHECK_EQUAL(a != d, true);
	BOOST_CHECK_EQUAL(a == e, false);
	BOOST_CHECK_EQUAL(a != e, true);

	/* everything different */
	BOOST_CHECK_EQUAL(a == f, false);
	BOOST_CHECK_EQUAL(a != f, true);
}

BOOST_AUTO_TEST_CASE( CFxpScalarMultiplication )
{
	CFxp a(1, 2, 8);
	Fxp b(2, 5);

	/* multiplication by scalar is commutative */
	BOOST_CHECK_EQUAL(a * b, b * a);

	/* width after multiplication by scalar */
	BOOST_CHECK_EQUAL((a * b).width(), 13);

	/* result check */
	BOOST_CHECK_EQUAL(a * b, CFxp(2, 4, 13));
}

BOOST_AUTO_TEST_CASE( CFxpMultiplication )
{
	CFxp a(1, 2, 8);
	CFxp b(2, 5, 5);

	/* complex multiplication is commutative */
	BOOST_CHECK_EQUAL(a * b, b * a);

	/* chaining works */
	BOOST_CHECK_EQUAL(a * a * a, a * a * a);

	/* width after multiplication */
	BOOST_CHECK_EQUAL((a * b).width(), 14);

	/* result check */
	BOOST_CHECK_EQUAL(a * b, CFxp(-8, 9, 14));
}

BOOST_AUTO_TEST_CASE( CFxpAddition )
{
	CFxp a(1, 2, 8);
	CFxp b(2, 5, 5);

	/* addition is commutative */
	BOOST_CHECK_EQUAL(a + b, b + a);

	/* chaining works */
	BOOST_CHECK_EQUAL(a + a + a, a + a + a);

	/* width after addition */
	BOOST_CHECK_EQUAL((a + b).width(), 9);

	/* result check */
	BOOST_CHECK_EQUAL(a + b, CFxp(3, 7, 9));
}

BOOST_AUTO_TEST_CASE( CFxpTruncation )
{
	CFxp a(15, -32, 10);

	BOOST_CHECK_EQUAL(a.truncateBy(2), CFxp(3, -8, 8));
	BOOST_CHECK_EQUAL(a.truncateTo(7), CFxp(1, -4, 7));
}

BOOST_AUTO_TEST_CASE( CFxpRounding )
{
	CFxp a(15, -32, 10);

	BOOST_CHECK_EQUAL(a.roundBy(2), CFxp(4, -8, 8));
	BOOST_CHECK_EQUAL(a.roundTo(7), CFxp(2, -4, 7));
}
<commit_msg>CFxp: Added missing unit tests for all resizing operations<commit_after>#include "boost_test.h"
#include "ComplexFixedPoint.h"
#include <complex>

using namespace std;

BOOST_AUTO_TEST_CASE( CFxpConstructors )
{
	/* Check constructors */
	CFxp b(1, -3, 4);
	BOOST_CHECK_EQUAL(b.width(), 4);
	BOOST_CHECK_EQUAL(b.real(), 1);
	BOOST_CHECK_EQUAL(b.imag(), -3);
	CFxp c(complex<int64_t>(5, -13), 6);
	BOOST_CHECK_EQUAL(c.width(), 6);
	BOOST_CHECK_EQUAL(c.real(), 5);
	BOOST_CHECK_EQUAL(c.imag(), -13);

	/* Values too large for width */
	BOOST_CHECK_THROW(CFxp(128, 0, 8), range_error);
	BOOST_CHECK_THROW(CFxp(0, -129, 8), range_error);

	/* Width larger than max allowed width */
	BOOST_CHECK_THROW(CFxp(0, 0, CFxp::MAX_WIDTH + 1), range_error);
}

BOOST_AUTO_TEST_CASE( CFxpAccessors )
{
	CFxp a(1, 2, 8);

	BOOST_CHECK_EQUAL(a.real(), 1);
	BOOST_CHECK_EQUAL(a.imag(), 2);
	BOOST_CHECK_EQUAL(a.width(), 8);
	BOOST_CHECK_EQUAL(a.minVal(), -128);
	BOOST_CHECK_EQUAL(a.maxVal(), 127);
}

BOOST_AUTO_TEST_CASE( CFxpAssignment )
{
	CFxp a(1, 2, 8);
	CFxp b(1, 2, 10);
	CFxp c(0, 0, 8);

	/* lhs and rhs widths must match by default */
	BOOST_CHECK_THROW(a = b, SizeMismatchException);

	/* Valid assignment */
	BOOST_CHECK_NO_THROW(c = a);
	BOOST_CHECK_EQUAL(c, a);
}

BOOST_AUTO_TEST_CASE( CFxpEquality )
{
	CFxp a(1, 2, 8);
	CFxp b(1, 2, 8);
	CFxp c(1, 2, 10);
	CFxp d(13, 2, 8);
	CFxp e(1, 13, 8);
	CFxp f(24, 38, 21);

	/* same object as lhs and rhs */
	BOOST_CHECK_EQUAL(a == a, true);
	BOOST_CHECK_EQUAL(a != a, false);

	/* different equal objects */
	BOOST_CHECK_EQUAL(a == b, true);
	BOOST_CHECK_EQUAL(a != b, false);

	/* same value, different widths */
	BOOST_CHECK_EQUAL(a == c, false);
	BOOST_CHECK_EQUAL(a != c, true);

	/* different values, same widths */
	BOOST_CHECK_EQUAL(a == d, false);
	BOOST_CHECK_EQUAL(a != d, true);
	BOOST_CHECK_EQUAL(a == e, false);
	BOOST_CHECK_EQUAL(a != e, true);

	/* everything different */
	BOOST_CHECK_EQUAL(a == f, false);
	BOOST_CHECK_EQUAL(a != f, true);
}

BOOST_AUTO_TEST_CASE( CFxpScalarMultiplication )
{
	CFxp a(1, 2, 8);
	Fxp b(2, 5);

	/* multiplication by scalar is commutative */
	BOOST_CHECK_EQUAL(a * b, b * a);

	/* width after multiplication by scalar */
	BOOST_CHECK_EQUAL((a * b).width(), 13);

	/* result check */
	BOOST_CHECK_EQUAL(a * b, CFxp(2, 4, 13));
}

BOOST_AUTO_TEST_CASE( CFxpMultiplication )
{
	CFxp a(1, 2, 8);
	CFxp b(2, 5, 5);

	/* complex multiplication is commutative */
	BOOST_CHECK_EQUAL(a * b, b * a);

	/* chaining works */
	BOOST_CHECK_EQUAL(a * a * a, a * a * a);

	/* width after multiplication */
	BOOST_CHECK_EQUAL((a * b).width(), 14);

	/* result check */
	BOOST_CHECK_EQUAL(a * b, CFxp(-8, 9, 14));
}

BOOST_AUTO_TEST_CASE( CFxpAddition )
{
	CFxp a(1, 2, 8);
	CFxp b(2, 5, 5);

	/* addition is commutative */
	BOOST_CHECK_EQUAL(a + b, b + a);

	/* chaining works */
	BOOST_CHECK_EQUAL(a + a + a, a + a + a);

	/* width after addition */
	BOOST_CHECK_EQUAL((a + b).width(), 9);

	/* result check */
	BOOST_CHECK_EQUAL(a + b, CFxp(3, 7, 9));
}

BOOST_AUTO_TEST_CASE( CFxpTruncation )
{
	CFxp a(15, -32, 10);

	/* Check results for normal use */
	BOOST_CHECK_EQUAL(a.truncateBy(2), CFxp(3, -8, 8));
	BOOST_CHECK_EQUAL(a.truncateTo(7), CFxp(1, -4, 7));

	/* Go beyond allowed range */
	BOOST_CHECK_THROW(a.truncateTo(0), std::range_error);
	BOOST_CHECK_THROW(a.truncateBy(a.width()), std::range_error);
}

BOOST_AUTO_TEST_CASE( CFxpRounding )
{
	CFxp a(15, -32, 10);

	/* Check results for normal use */
	BOOST_CHECK_EQUAL(a.roundBy(2), CFxp(4, -8, 8));
	BOOST_CHECK_EQUAL(a.roundTo(7), CFxp(2, -4, 7));

	/* Go beyond allowed range */
	BOOST_CHECK_THROW(a.roundTo(0), std::range_error);
	BOOST_CHECK_THROW(a.roundBy(a.width()), std::range_error);
}

BOOST_AUTO_TEST_CASE( CFxpSaturation )
{
	CFxp a(432, -397, 10);

	/* Check results for normal use */
	BOOST_CHECK_EQUAL(a.saturateBy(2), CFxp(127, -128, 8));
	BOOST_CHECK_EQUAL(a.saturateTo(6), CFxp(31, -32, 6));

	/* Go beyond allowed range */
	BOOST_CHECK_THROW(a.saturateTo(0), std::range_error);
	BOOST_CHECK_THROW(a.saturateBy(a.width()), std::range_error);
}

BOOST_AUTO_TEST_CASE( CFxpSignExtension )
{
	CFxp a(15, -32, 10);

	/* Check results for normal use */
	BOOST_CHECK_EQUAL(a.signExtendBy(2), CFxp(15, -32, 12));
	BOOST_CHECK_EQUAL(a.signExtendTo(51), CFxp(15, -32, 51));

	/* Try to sign extend beyond allowed range */
	BOOST_CHECK_THROW(a.signExtendTo(CFxp::MAX_WIDTH + 1), std::range_error);
	BOOST_CHECK_THROW(a.signExtendBy(CFxp::MAX_WIDTH - a.width() + 1), std::range_error);
}
<|endoftext|>


<commit_before><commit_msg>Save coefficient of restitution to JSON.<commit_after><|endoftext|>


<commit_before>/*******************************************************************************
    Copyright 2018,2019 Google LLC

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        https://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*******************************************************************************/

#pragma once

#include <cstdint>
#include <cstddef>
#include <cstdbool>

#include <vector>

namespace driver {

struct EventInfo {
    int irq_n;
    int evt_id;
    void* src;
    void* data;
};

class EventHandler {
    public:
        virtual void handle_event(EventInfo* e_info) = 0;
};

class Peripheral {
    public:
        using HandlerContainerT = std::vector<EventHandler*>;
        Peripheral() : base_{0}, irq_n_{0} {};
        Peripheral(uint32_t base, unsigned int irq_n) : base_ {base}, irq_n_{irq_n} {}
        Peripheral(uint32_t base, unsigned int irq_n, HandlerContainerT* evt_handlers);
        Peripheral(const Peripheral&) = delete;

        uint32_t get_base() const {
            return base_;
        }

        unsigned int get_irq_num() const {
            return irq_n_;
        }

        int set_irq_handler(void (*handler)(void)) const;
        void enable_irq() const;
        void disable_irq() const;

        void handle_events();
        int add_event_handler(unsigned int evt, EventHandler* handler);

        virtual void enable_interrupts(uint32_t) {};
        virtual void disable_interrupts(uint32_t) {};

    protected:
        virtual bool is_event_active(int evt) const;
        virtual void clear_event(int evt);

        const uint32_t base_;
        const unsigned int irq_n_;

    private:

        HandlerContainerT* evt_handlers_ = nullptr;
};

}  // namespace driver
<commit_msg>driver: peripheral: change EventInfo<commit_after>/*******************************************************************************
    Copyright 2018,2019 Google LLC

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        https://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*******************************************************************************/

#pragma once

#include <cstdint>
#include <cstddef>
#include <cstdbool>

#include <vector>

namespace driver {

struct EventInfo {
    int irq_n;
    int evt_id;
    volatile void* src;
    const void* data;
};

class EventHandler {
    public:
        virtual void handle_event(EventInfo* e_info) = 0;
};

class Peripheral {
    public:
        using HandlerContainerT = std::vector<EventHandler*>;
        Peripheral() : base_{0}, irq_n_{0} {};
        Peripheral(uint32_t base, unsigned int irq_n) : base_ {base}, irq_n_{irq_n} {}
        Peripheral(uint32_t base, unsigned int irq_n, HandlerContainerT* evt_handlers);
        Peripheral(const Peripheral&) = delete;

        uint32_t get_base() const {
            return base_;
        }

        unsigned int get_irq_num() const {
            return irq_n_;
        }

        int set_irq_handler(void (*handler)(void)) const;
        void enable_irq() const;
        void disable_irq() const;

        void handle_events();
        int add_event_handler(unsigned int evt, EventHandler* handler);

        virtual void enable_interrupts(uint32_t) {};
        virtual void disable_interrupts(uint32_t) {};

    protected:
        virtual bool is_event_active(int evt) const;
        virtual void clear_event(int evt);

        const uint32_t base_;
        const unsigned int irq_n_;

    private:

        HandlerContainerT* evt_handlers_ = nullptr;
};

}  // namespace driver
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2015, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file ConfigUpdateListener.hxx
 *
 * Interface for receiving configuration from EEPROM, including callback at
 * boot time and updates to this configuration when the config is modified.
 *
 * @author Balazs Racz
 * @date 31 May 2014
 */

#ifndef _UTILS_CONFIGUPDATELISTENER_HXX_
#define _UTILS_CONFIGUPDATELISTENER_HXX_

#include "executor/Notifiable.hxx"

/// Abstract class for components that need to receive configuration from
/// EEPROM. Configuration update comes in two flavors: initial load and refresh
/// (upon potential change). Each configuration update call supplies the file
/// desriptor of the EEPROM file, which is already opened for reading.
///
/// Config listeners provide feedback to the system in two forms: they notify
/// the system in case the configuration change can be applied only with a
/// reboot of the hardware node, or whether an application-level
/// reinitialization sequence is needed.
class ConfigUpdateListener : public QMember
{
public:
    /// Specifies what additional steps are needed to apply the new
    /// configuration.
    enum UpdateAction
    {
        /// No additional step is necessary.
        UPDATED = 0,
        /// The call needs to be re-tried.
        RETRY,
        /// Need to perform application-level reinitialization. (In case of
        /// OpenLCB this means an identify all events procedure is needed.)
        REINIT_NEEDED,
        /// Need to reboot the hardware.
        REBOOT_NEEDED,
    };

    /// Notifies the component that there is new configuration available for
    /// loading.
    ///
    /// The call is made on the main executor, so the call must not
    /// block. Reading the given EEPROM device should be fine. Asynchronous
    /// operations may be implemented by using a special return status RETRY:
    /// the runner will call the same method on the same component once more
    /// after the done callback is invoked. This allows implementing state
    /// machines.
    ///
    /// @param fd is the file descriptor for the EEPROM file. The current
    /// offset in this file is unspecified, callees must do lseek.
    /// @param initial_load is true if this is the first load upon starting the
    /// binary.
    /// @param done must be notified when the call and its dependent actions
    /// are complete. No other configuration component will be called until the
    /// done callback is invoked.
    ///
    /// @return any necessary action. If returns UPDATED, then assumes that the
    /// configuration change was applied. If returns RETRY, then the same call
    /// will be made again after the notifiable is called. If return
    /// REINIT_NEEDED or REBOOT_NEEDED then at the end of the configuration
    /// update process the node will be reinitialized or rebooted accordingly.
    virtual UpdateAction apply_configuration(int fd, bool initial_load,
                                             BarrierNotifiable *done) = 0;

    /// Clears configuration file and resets the configuration settings to
    /// factory value.
    ///
    /// @param fd is the file descriptor for the EEPROM file. The current
    /// offset in this file is unspecified, callees must do lseek.
    virtual void factory_reset(int fd) = 0;
};

#endif // _UTILS_CONFIGUPDATELISTENER_HXX_
<commit_msg>Fixes broken compile<commit_after>/** \copyright
 * Copyright (c) 2015, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file ConfigUpdateListener.hxx
 *
 * Interface for receiving configuration from EEPROM, including callback at
 * boot time and updates to this configuration when the config is modified.
 *
 * @author Balazs Racz
 * @date 31 May 2014
 */

#ifndef _UTILS_CONFIGUPDATELISTENER_HXX_
#define _UTILS_CONFIGUPDATELISTENER_HXX_

#include "utils/QMember.hxx"
#include "executor/Notifiable.hxx"

/// Abstract class for components that need to receive configuration from
/// EEPROM. Configuration update comes in two flavors: initial load and refresh
/// (upon potential change). Each configuration update call supplies the file
/// desriptor of the EEPROM file, which is already opened for reading.
///
/// Config listeners provide feedback to the system in two forms: they notify
/// the system in case the configuration change can be applied only with a
/// reboot of the hardware node, or whether an application-level
/// reinitialization sequence is needed.
class ConfigUpdateListener : public QMember
{
public:
    /// Specifies what additional steps are needed to apply the new
    /// configuration.
    enum UpdateAction
    {
        /// No additional step is necessary.
        UPDATED = 0,
        /// The call needs to be re-tried.
        RETRY,
        /// Need to perform application-level reinitialization. (In case of
        /// OpenLCB this means an identify all events procedure is needed.)
        REINIT_NEEDED,
        /// Need to reboot the hardware.
        REBOOT_NEEDED,
    };

    /// Notifies the component that there is new configuration available for
    /// loading.
    ///
    /// The call is made on the main executor, so the call must not
    /// block. Reading the given EEPROM device should be fine. Asynchronous
    /// operations may be implemented by using a special return status RETRY:
    /// the runner will call the same method on the same component once more
    /// after the done callback is invoked. This allows implementing state
    /// machines.
    ///
    /// @param fd is the file descriptor for the EEPROM file. The current
    /// offset in this file is unspecified, callees must do lseek.
    /// @param initial_load is true if this is the first load upon starting the
    /// binary.
    /// @param done must be notified when the call and its dependent actions
    /// are complete. No other configuration component will be called until the
    /// done callback is invoked.
    ///
    /// @return any necessary action. If returns UPDATED, then assumes that the
    /// configuration change was applied. If returns RETRY, then the same call
    /// will be made again after the notifiable is called. If return
    /// REINIT_NEEDED or REBOOT_NEEDED then at the end of the configuration
    /// update process the node will be reinitialized or rebooted accordingly.
    virtual UpdateAction apply_configuration(int fd, bool initial_load,
                                             BarrierNotifiable *done) = 0;

    /// Clears configuration file and resets the configuration settings to
    /// factory value.
    ///
    /// @param fd is the file descriptor for the EEPROM file. The current
    /// offset in this file is unspecified, callees must do lseek.
    virtual void factory_reset(int fd) = 0;
};

#endif // _UTILS_CONFIGUPDATELISTENER_HXX_
<|endoftext|>


<commit_before>#include <sys/signalfd.h>

#include "zlog/zlog.hpp"

#include "exemodel/poll_tools.hpp"
#include "exemodel/signalee.hpp"

namespace exemodel {

void mask_signal(int signum)
{
	sigset_t mask;
	sigemptyset(&mask);
	sigaddset(&mask, signum);

	int ret = ::sigprocmask(SIG_BLOCK, &mask, NULL);
	validate_ret(ret, "sigprocmask");
}

static int gen_signalfd(int signum)
{
	sigset_t mask;
	sigemptyset(&mask);
	sigaddset(&mask, signum);

	int sfd = ::signalfd(-1, &mask, 0);
	validate_ret(sfd, "generate signal fd");

	return sfd;
}

signalee::signalee(int signum)
: pollee(gen_signalfd(signum),
	 (uint32_t)(::EPOLLIN),
	 "signalee")
{
}

signalee::~signalee()
{
	/// \todo need deblock signum
}

void signalee::dispose(poller &, uint32_t evts)
{
	if ((evts & ::EPOLLIN)) {
		struct signalfd_siginfo fdsi;
		ssize_t ret = this->read(&fdsi, sizeof(fdsi));
		if (ret == sizeof(fdsi)) {
			this->exe();
		}
	}
}

}
<commit_msg>fix validate for fd<commit_after>#include <sys/signalfd.h>

#include "zlog/zlog.hpp"

#include "exemodel/poll_tools.hpp"
#include "exemodel/signalee.hpp"

namespace exemodel {

void mask_signal(int signum)
{
	sigset_t mask;
	sigemptyset(&mask);
	sigaddset(&mask, signum);

	int ret = ::sigprocmask(SIG_BLOCK, &mask, NULL);
	validate_ret(ret, "sigprocmask");
}

static int gen_signalfd(int signum)
{
	sigset_t mask;
	sigemptyset(&mask);
	sigaddset(&mask, signum);

	int sfd = ::signalfd(-1, &mask, 0);
	validate_fd(sfd, "generate signal fd");

	return sfd;
}

signalee::signalee(int signum)
: pollee(gen_signalfd(signum),
	 (uint32_t)(::EPOLLIN),
	 "signalee")
{
}

signalee::~signalee()
{
	/// \todo need deblock signum
}

void signalee::dispose(poller &, uint32_t evts)
{
	if ((evts & ::EPOLLIN)) {
		struct signalfd_siginfo fdsi;
		ssize_t ret = this->read(&fdsi, sizeof(fdsi));
		if (ret == sizeof(fdsi)) {
			this->exe();
		}
	}
}

}
<|endoftext|>