<commit_before>#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <geometry_msgs/PoseStamped.h>
#include <tf/transform_listener.h>

#define METRE_TO_PIXEL_SCALE 50
#define FORWARD_SWIM_SPEED_SCALING 0.1
#define POSITION_GRAPHIC_RADIUS 20.0
#define HEADING_GRAPHIC_LENGTH 50.0

// Class Localizer is a sample stub that you can build upon for your implementation
// (advised but optional: starting from scratch is also fine)
//
class Localizer
{
public:
  ros::NodeHandle nh;
  image_transport::Publisher pub;
  image_transport::Subscriber gt_img_sub;
  image_transport::Subscriber robot_img_sub;

  ros::Subscriber motion_command_sub;

  geometry_msgs::PoseStamped estimated_location;

  cv::Mat map_image;
  cv::Mat ground_truth_image;
  cv::Mat localization_result_image;

  Localizer( int argc, char** argv )
  {
    image_transport::ImageTransport it(nh);
    pub = it.advertise("/assign2/localization_result_image", 1);
    map_image = cv::imread(argv[1], CV_LOAD_IMAGE_COLOR);

    estimated_location.pose.position.x = 0;
    estimated_location.pose.position.y = 0;

    localization_result_image = map_image.clone();
    ground_truth_image = map_image.clone();

    gt_img_sub = it.subscribe("/assign2/ground_truth_image", 1, &Localizer::groundTruthImageCallback, this);
    robot_img_sub = it.subscribe("/aqua/back_down/image_raw", 1, &Localizer::robotImageCallback, this);
    motion_command_sub = nh.subscribe<geometry_msgs::PoseStamped>("/aqua/target_pose", 1, &Localizer::motionCommandCallback, this);

    ROS_INFO( "localizer node constructed and subscribed." );

  }

  void robotImageCallback( const sensor_msgs::ImageConstPtr& robot_img )
  {
    // TODO: You must fill in the code here to implement an observation model for your localizer
  }

  // Function motionCommandCallback is a example of how to work with Aqua's motion commands (your view on the odometry).
  // The initial version simply integrates the commands over time to make a very rough location estimate.
  // TODO: You must improve it to work with the localizer you implement.
  //
  // Note the somewhat unique meaning of fields in motion_command
  //    motion_command
  //      pose
  //        position
  //          x - requested forward swim effort in a unitless number ranging from 0.0 to 1.0. You must translate this into
  //              a velocity estimate in some way. Currently a simple constant is used.
  //          y - requested up/down swim effort. Not used in this assignment
  //          z - unused
  //        orientation - A quaternion that represents the desired body orientation w.r.t. global frame. Note that
  //                      Aqua's controller will not achieve this orientation perfectly, so you must consider that
  //                      this is only a noisy estimate of the robot's orientation (correct for it with your filter!)
  //
  // Note that we use negative angles because the geometry of the map image is formed with its Z axis pointing downwards
  // (the camera is looking at the ocean floor)
  //
  void motionCommandCallback(const geometry_msgs::PoseStamped::ConstPtr& motion_command )
  {

    geometry_msgs::PoseStamped command = *motion_command;
    double target_roll, target_pitch, target_yaw;
    tf::Quaternion target_orientation;
    tf::quaternionMsgToTF(command.pose.orientation, target_orientation);
    tf::Matrix3x3(target_orientation).getEulerYPR( target_yaw, target_pitch, target_roll );

    // The following three lines implement the basic motion model example
    estimated_location.pose.position.x = estimated_location.pose.position.x + FORWARD_SWIM_SPEED_SCALING * command.pose.position.x * cos( -target_yaw );
    estimated_location.pose.position.y = estimated_location.pose.position.y + FORWARD_SWIM_SPEED_SCALING * command.pose.position.x * sin( -target_yaw );
    estimated_location.pose.orientation = command.pose.orientation;

    // The remainder of this function is sample drawing code to plot your answer on the map image.

    // Comment the one following line to plot your whole trajectory without ground truth
    localization_result_image = ground_truth_image.clone();

    int estimated_robo_image_x = localization_result_image.size().width/2 + METRE_TO_PIXEL_SCALE * estimated_location.pose.position.x;
    int estimated_robo_image_y = localization_result_image.size().height/2 + METRE_TO_PIXEL_SCALE * estimated_location.pose.position.y;

    int estimated_heading_image_x = estimated_robo_image_x + HEADING_GRAPHIC_LENGTH * cos(-target_yaw);
    int estimated_heading_image_y = estimated_robo_image_y + HEADING_GRAPHIC_LENGTH * sin(-target_yaw);

    cv::circle( localization_result_image, cv::Point(estimated_robo_image_x, estimated_robo_image_y), POSITION_GRAPHIC_RADIUS, CV_RGB(250,0,0), -1);
    cv::line( localization_result_image, cv::Point(estimated_robo_image_x, estimated_robo_image_y), cv::Point(estimated_heading_image_x, estimated_heading_image_y), CV_RGB(250,0,0), 10);
  }

  // This is a provided convenience function that allows you to compare your localization result to a ground truth path
  void groundTruthImageCallback( const sensor_msgs::ImageConstPtr& gt_img )
  {
    cv_bridge::CvImagePtr cv_ptr;
    try
    {
      cv_ptr = cv_bridge::toCvCopy(gt_img, sensor_msgs::image_encodings::BGR8);
    }
    catch (cv_bridge::Exception& e)
    {
      ROS_ERROR("cv_bridge exception: %s", e.what());
      return;
    }

    ground_truth_image = cv::Mat(cv_ptr->image);
  }

  // This function publishes your localization result image and spins ROS to execute its callbacks
  void spin()
  {
    ros::Rate loop_rate(30);
    while (nh.ok()) {
      sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", localization_result_image).toImageMsg();
      pub.publish(msg);

      ros::spinOnce();
      loop_rate.sleep();
    }
  }
};

int main(int argc, char** argv)
{
  ros::init(argc, argv, "localizer");
  Localizer my_loc(argc, argv);
  my_loc.spin();
}
<commit_msg>added CV stuff<commit_after>#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <geometry_msgs/PoseStamped.h>
#include <tf/transform_listener.h>
#include <opencv2/opencv.hpp>

#define METRE_TO_PIXEL_SCALE 50
#define FORWARD_SWIM_SPEED_SCALING 0.1
#define POSITION_GRAPHIC_RADIUS 20.0
#define HEADING_GRAPHIC_LENGTH 50.0

// Class Localizer is a sample stub that you can build upon for your implementation
// (advised but optional: starting from scratch is also fine)
//
class Localizer
{
public:
  ros::NodeHandle nh;
  image_transport::Publisher pub;
  image_transport::Subscriber gt_img_sub;
  image_transport::Subscriber robot_img_sub;

  ros::Subscriber motion_command_sub;

  geometry_msgs::PoseStamped estimated_location;

  cv::Mat map_image;
  cv::Mat ground_truth_image;
  cv::Mat localization_result_image;

  Localizer( int argc, char** argv )
  {
    image_transport::ImageTransport it(nh);
    pub = it.advertise("/assign2/localization_result_image", 1);
    map_image = cv::imread(argv[1], CV_LOAD_IMAGE_COLOR);

    estimated_location.pose.position.x = 0;
    estimated_location.pose.position.y = 0;

    localization_result_image = map_image.clone();
    ground_truth_image = map_image.clone();

    gt_img_sub = it.subscribe("/assign2/ground_truth_image", 1, &Localizer::groundTruthImageCallback, this);
    robot_img_sub = it.subscribe("/aqua/back_down/image_raw", 1, &Localizer::robotImageCallback, this);
    motion_command_sub = nh.subscribe<geometry_msgs::PoseStamped>("/aqua/target_pose", 1, &Localizer::motionCommandCallback, this);

    ROS_INFO( "localizer node constructed and subscribed." );

  }

    // Compare two images by getting the L2 error (square-root of sum of squared error).
  double getSimilarity( const Mat A, const Mat B ) {
  if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
      // Calculate the L2 relative error between images.
      double errorL2 = norm( A, B, CV_L2 );
      // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
      double similarity = errorL2 / (double)( A.rows * A.cols );
      return similarity;
  }
  else {
      //Images have a different size
      return 100000000.0;  // Return a bad value
  }

  void robotImageCallback( const sensor_msgs::ImageConstPtr& robot_img )
  {

    cv::Mat rgb_img = cv_bridge::toCvShare(robot_img, "bgr8")->image;
    // TODO: You must fill in the code here to implement an observation model for your localizer
  }

  // Function motionCommandCallback is a example of how to work with Aqua's motion commands (your view on the odometry).
  // The initial version simply integrates the commands over time to make a very rough location estimate.
  // TODO: You must improve it to work with the localizer you implement.
  //
  // Note the somewhat unique meaning of fields in motion_command
  //    motion_command
  //      pose
  //        position
  //          x - requested forward swim effort in a unitless number ranging from 0.0 to 1.0. You must translate this into
  //              a velocity estimate in some way. Currently a simple constant is used.
  //          y - requested up/down swim effort. Not used in this assignment
  //          z - unused
  //        orientation - A quaternion that represents the desired body orientation w.r.t. global frame. Note that
  //                      Aqua's controller will not achieve this orientation perfectly, so you must consider that
  //                      this is only a noisy estimate of the robot's orientation (correct for it with your filter!)
  //
  // Note that we use negative angles because the geometry of the map image is formed with its Z axis pointing downwards
  // (the camera is looking at the ocean floor)
  //
  void motionCommandCallback(const geometry_msgs::PoseStamped::ConstPtr& motion_command )
  {

    geometry_msgs::PoseStamped command = *motion_command;
    double target_roll, target_pitch, target_yaw;
    tf::Quaternion target_orientation;
    tf::quaternionMsgToTF(command.pose.orientation, target_orientation);
    tf::Matrix3x3(target_orientation).getEulerYPR( target_yaw, target_pitch, target_roll );

    // The following three lines implement the basic motion model example
    estimated_location.pose.position.x = estimated_location.pose.position.x + FORWARD_SWIM_SPEED_SCALING * command.pose.position.x * cos( -target_yaw );
    estimated_location.pose.position.y = estimated_location.pose.position.y + FORWARD_SWIM_SPEED_SCALING * command.pose.position.x * sin( -target_yaw );
    estimated_location.pose.orientation = command.pose.orientation;

    // The remainder of this function is sample drawing code to plot your answer on the map image.

    // Comment the one following line to plot your whole trajectory without ground truth
    localization_result_image = ground_truth_image.clone();
    //we need to get the estimated_robo_image right
    int estimated_robo_image_x = localization_result_image.size().width/2 + METRE_TO_PIXEL_SCALE * estimated_location.pose.position.x;
    int estimated_robo_image_y = localization_result_image.size().height/2 + METRE_TO_PIXEL_SCALE * estimated_location.pose.position.y;

    int estimated_heading_image_x = estimated_robo_image_x + HEADING_GRAPHIC_LENGTH * cos(-target_yaw);
    int estimated_heading_image_y = estimated_robo_image_y + HEADING_GRAPHIC_LENGTH * sin(-target_yaw);

    cv::circle( localization_result_image, cv::Point(estimated_robo_image_x, estimated_robo_image_y), POSITION_GRAPHIC_RADIUS, CV_RGB(250,0,0), -1);
    cv::line( localization_result_image, cv::Point(estimated_robo_image_x, estimated_robo_image_y), cv::Point(estimated_heading_image_x, estimated_heading_image_y), CV_RGB(250,0,0), 10);
  }

  // This is a provided convenience function that allows you to compare your localization result to a ground truth path
  void groundTruthImageCallback( const sensor_msgs::ImageConstPtr& gt_img )
  {
    cv_bridge::CvImagePtr cv_ptr;
    try
    {
      cv_ptr = cv_bridge::toCvCopy(gt_img, sensor_msgs::image_encodings::BGR8);
    }
    catch (cv_bridge::Exception& e)
    {
      ROS_ERROR("cv_bridge exception: %s", e.what());
      return;
    }

    ground_truth_image = cv::Mat(cv_ptr->image);
  }

  // This function publishes your localization result image and spins ROS to execute its callbacks
  void spin()
  {
    ros::Rate loop_rate(30);
    while (nh.ok()) {
      sensor_msgs::ImagePtr msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", localization_result_image).toImageMsg();
      pub.publish(msg);

      ros::spinOnce();
      loop_rate.sleep();
    }
  }
};

int main(int argc, char** argv)
{
  ros::init(argc, argv, "localizer");
  Localizer my_loc(argc, argv);
  my_loc.spin();
}
<|endoftext|>


<commit_before>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
* Copyright (c) 2011 Ruslan Kabatsayev <b7.10110111@gmail.com>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygenwindecooptions.h"
#include "oxygenstyle.h"
#include "oxygenwindowshadow.h"

namespace Oxygen
{
    namespace WinDeco
    {

        gint getMetric(Metric wm)
        {
            const int frameBorder(Style::instance().settings().frameBorder());
            const bool narrowSpacing(false); // TODO: false -> read configuration
            const bool compositingActive(false); // TODO: implement check for this
            const bool hideTitleBar(false); // TODO: implement

            switch (wm)
            {
                case BorderLeft:
                case BorderRight:
                case BorderBottom:
                {
                    gint border(0);
                    // for now we think that respectWindowState is always true,
                    // and WM will remove borders for maximized windows by defaul
                    // TODO: impelement respectWindowState
                    if( wm==BorderBottom && frameBorder >= QtSettings::BorderNoSide ) {

                        // for tiny border, the convention is to have a larger bottom area in order to
                        // make resizing easier
                        border = std::max(frameBorder,4);
                    }
                    else if( frameBorder < QtSettings::BorderTiny ) {

                        border = 0;
                    }
                    else if( compositingActive && frameBorder == QtSettings::BorderTiny ) {

                        border = std::max( frameBorder, 3 );
                    }
                    else {

                        border = frameBorder;
                    }

                    return border;
                }

                case BorderTop:
                {
                    gint topEdge(0);
                    if( frameBorder == QtSettings::BorderNone && hideTitleBar )
                    {
                        topEdge=0;
                    }
                    else
                    {
                        topEdge=3; // TFRAMESIZE
                    }

                    return Style::instance().settings().buttonSize()+topEdge;
                }

                case ButtonSpacing:
                {
                    return ( narrowSpacing ? 1 : 3 );
                }

                case ButtonMarginTop:
                case ButtonMarginBottom:
                {
                    return 0;
                }

                case ShadowLeft:
                case ShadowRight:
                case ShadowTop:
                case ShadowBottom:
                {
                    WindowShadow shadow(Style::instance().settings(), Style::instance().helper());
                    return int(shadow.shadowSize());
                }

                default:
                {
                    // -1 means we don't understand the query
                    return -1;
                }
            }
        }

        int getButtonSize()
        {
            // in oxygen all buttons have the same size
            return Style::instance().settings().buttonSize();
        }
    }
}
<commit_msg>Fix shadow size returned<commit_after>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
* Copyright (c) 2011 Ruslan Kabatsayev <b7.10110111@gmail.com>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygenwindecooptions.h"
#include "oxygenstyle.h"
#include "oxygenwindowshadow.h"

namespace Oxygen
{
    namespace WinDeco
    {

        gint getMetric(Metric wm)
        {
            const int frameBorder(Style::instance().settings().frameBorder());
            const bool narrowSpacing(false); // TODO: false -> read configuration
            const bool compositingActive(false); // TODO: implement check for this
            const bool hideTitleBar(false); // TODO: implement

            switch (wm)
            {
                case BorderLeft:
                case BorderRight:
                case BorderBottom:
                {
                    gint border(0);
                    // for now we think that respectWindowState is always true,
                    // and WM will remove borders for maximized windows by defaul
                    // TODO: impelement respectWindowState
                    if( wm==BorderBottom && frameBorder >= QtSettings::BorderNoSide ) {

                        // for tiny border, the convention is to have a larger bottom area in order to
                        // make resizing easier
                        border = std::max(frameBorder,4);
                    }
                    else if( frameBorder < QtSettings::BorderTiny ) {

                        border = 0;
                    }
                    else if( compositingActive && frameBorder == QtSettings::BorderTiny ) {

                        border = std::max( frameBorder, 3 );
                    }
                    else {

                        border = frameBorder;
                    }

                    return border;
                }

                case BorderTop:
                {
                    gint topEdge(0);
                    if( frameBorder == QtSettings::BorderNone && hideTitleBar )
                    {
                        topEdge=0;
                    }
                    else
                    {
                        topEdge=3; // TFRAMESIZE
                    }

                    return Style::instance().settings().buttonSize()+topEdge;
                }

                case ButtonSpacing:
                {
                    return ( narrowSpacing ? 1 : 3 );
                }

                case ButtonMarginTop:
                case ButtonMarginBottom:
                {
                    return 0;
                }

                case ShadowLeft:
                case ShadowRight:
                case ShadowTop:
                case ShadowBottom:
                {
                    WindowShadow shadow(Style::instance().settings(), Style::instance().helper());
                    return int(shadow.shadowSize()-4);
                }

                default:
                {
                    // -1 means we don't understand the query
                    return -1;
                }
            }
        }

        int getButtonSize()
        {
            // in oxygen all buttons have the same size
            return Style::instance().settings().buttonSize();
        }
    }
}
<|endoftext|>


<commit_before>#include <nexus_types.hpp>
#include <sstream>
#include <string>

namespace nexus {

slave_id slaveID_CPP2C(SlaveID sid) {
  return sid.c_str();
}

SlaveID slaveID_C2CPP(slave_id sid) {
  return std::string(sid);
}

}
<commit_msg>Removed unnecessary file (signed off by Ali).<commit_after><|endoftext|>


<commit_before>/******************************************************************************
 *
 * Copyright (C) 1990-2018, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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
 *
 *    http://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.
 *
 ******************************************************************************/

#include "condor_common.h"
#include "condor_debug.h"
#include "condor_config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#if defined( LINUX )
#include <sys/inotify.h>
#include <poll.h>
#endif /* defined( LINUX ) */

#include "utc_time.h"
#include "file_modified_trigger.h"

FileModifiedTrigger::FileModifiedTrigger( const std::string & f ) :
	filename( f ), initialized( false ),
	statfd( -1 ), lastSize( 0 )
{
	statfd = open( filename.c_str(), O_RDONLY );
	if( statfd == -1 ) {
		dprintf( D_ALWAYS, "FileModifiedTrigger( %s ): open() failed: %s (%d).\n", filename.c_str(), strerror(errno), errno );
		return;
	}

#if defined( LINUX )
	inotify_fd = inotify_init1( IN_NONBLOCK );
	if( inotify_fd == -1 ) {
		dprintf( D_ALWAYS, "FileModifiedTrigger( %s ): inotify_init() failed: %s (%d).\n", filename.c_str(), strerror(errno), errno );
		return;
	}

	int wd = inotify_add_watch( inotify_fd, filename.c_str(), IN_MODIFY );
	if( wd == -1 ) {
		dprintf( D_ALWAYS, "FileModifiedTrigger( %s ): inotify_add_watch() failed: %s (%d).\n", filename.c_str(), strerror( errno ), errno );
		return;
	}
#endif /* defined( LINUX ) */

	initialized = true;
}

FileModifiedTrigger::~FileModifiedTrigger() {
	if( initialized && statfd != -1 ) {
		close( statfd );
		statfd = -1;
	}

#if defined( LINUX )
	if( initialized && inotify_fd != -1 ) {
		close( inotify_fd );
		inotify_fd = -1;
	}
#endif /* defined( LINUX ) */
}

//
// We would like to support the Linux and Windows -specific nonpolling
// solutions for detecting changes to the log file, but inotify blocks
// forever on network filesystems, and there's no Linux API which will
// let us know that ahead of time.  Instead, we use a polling approach
// on all platforms, but instead of sleeping on some platforms, we use
// inotify with the sleep duration as its timeout.
//
// On Linux, this means that notify_or_sleep() "sleeps" in poll(), and
// if the inotify fd goes hot, uses read_inotify_events() to clear the
// fd for the next time.
//

#if defined( LINUX )

int
FileModifiedTrigger::read_inotify_events( void ) {
	// Magic from 'man inotify'.
	char buf[ sizeof(struct inotify_event) + NAME_MAX + 1 ]
		__attribute__ ((aligned(__alignof__(struct inotify_event))));

	while( true ) {
		ssize_t len = read( inotify_fd, buf, sizeof( buf ) );
		if( len == -1 && errno != EAGAIN ) {
			dprintf( D_ALWAYS, "FileModifiedTrigger::read_inotify_events(%s): failed to ready from inotify fd.\n", filename.c_str() );
			return -1;
		}

		// We're done reading events for now.
		if( len <= 0 ) { return 1; }

		char * ptr = buf;
		for( ; ptr < buf + len; ptr += sizeof(struct inotify_event) + ((struct inotify_event *)ptr)->len ) {
			const struct inotify_event * event = (struct inotify_event *)ptr;
			if(! (event->mask & IN_MODIFY) ) {
				dprintf( D_ALWAYS, "FileModifiedTrigger::read_inotify_events(%s): inotify gave me an event I didn't ask for.\n", filename.c_str() );
				return -1;
			}
		}

		// We don't worry about partial reads because we're only watching
		// one file for one event type, and the kernel will coalesce
		// identical events, so we should only ever see one. Nonetheless,
		// we'll verify here that we read only complete events.
		if( ptr != buf + len ) {
			dprintf( D_ALWAYS, "FileModifiedTrigger::read_inotify_events(%s): partial inotify read.\n", filename.c_str() );
			return -1;
		}
	}

	return 1;
}

int
FileModifiedTrigger::notify_or_sleep( int waitfor ) {
	struct pollfd pollfds[1];
	pollfds[0].fd = inotify_fd;
	pollfds[0].events = POLLIN;
	pollfds[0].revents = 0;

	int events = poll( pollfds, 1, waitfor );
	switch( events ) {
		case -1:
			return -1;

		case 0:
			return 0;

		default:
			if( pollfds[0].revents & POLLIN ) {
				return read_inotify_events();
			} else {
				dprintf( D_ALWAYS, "FileModifiedTrigger::wait(): inotify returned an event I didn't ask for.\n" );
				return -1;
			}
	}
}

#else

#if defined(WINDOWS)
int usleep( long long microseconds ) {
	HANDLE timer;
	LARGE_INTEGER timerIntervals = { microseconds * -10 };
	timer = CreateWaitableTimer( NULL, true, NULL );
	SetWaitableTimer( timer, & timerIntervals, 0, NULL, NULL, false );
	WaitForSingleObject( timer, INFINITE );
	CloseHandle( timer );
	return 0;
}
#endif /* defined(WINDOWS) */

int
FileModifiedTrigger::notify_or_sleep( int waitfor ) {
	return usleep( waitfor * 1000 );
}

#endif /* defined( LINUX ) */

int
FileModifiedTrigger::wait( int timeout ) {
	if(! initialized) {
		return -1;
	}

	struct timeval deadline;
	condor_gettimestamp( deadline );

	deadline.tv_sec += timeout / 1000;
	deadline.tv_usec += (timeout % 1000) * 1000;
	deadline.tv_usec = deadline.tv_usec % 1000000;

	while( true ) {
		struct stat statbuf;
		int rv = fstat( statfd, & statbuf );
		if( rv != 0 ) {
			dprintf( D_ALWAYS, "FileModifiedTrigger::wait(): fstat() failure on previously-valid fd: %s (%d).\n", strerror(errno), errno );
			return -1;
		}

		bool changed = statbuf.st_size != lastSize;
		lastSize = statbuf.st_size;
		if( changed ) { return 1; }

		struct timeval now;
		condor_gettimestamp( now );

		if( deadline.tv_sec < now.tv_sec ) { return 0; }
		else if( deadline.tv_sec == now.tv_sec &&
			deadline.tv_usec < now.tv_usec ) { return 0; }

		int waitfor = ((deadline.tv_sec - now.tv_sec) * 1000) +
						((deadline.tv_usec - now.tv_usec) / 1000);
		if( waitfor > 5000 ) { waitfor = 5000; }

		int events = notify_or_sleep( waitfor );
		if( events == 1 ) { return 1; }
		if( events == 0 ) { continue; }
		return -1;
	}

}
<commit_msg>(#7457)  Merge old-inotify patch.<commit_after>/******************************************************************************
 *
 * Copyright (C) 1990-2018, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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
 *
 *    http://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.
 *
 ******************************************************************************/

#include "condor_common.h"
#include "condor_debug.h"
#include "condor_config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#if defined( LINUX )
#include <sys/inotify.h>
#include <poll.h>
#endif /* defined( LINUX ) */

#include "utc_time.h"
#include "file_modified_trigger.h"

FileModifiedTrigger::FileModifiedTrigger( const std::string & f ) :
	filename( f ), initialized( false ),
	statfd( -1 ), lastSize( 0 )
{
	statfd = open( filename.c_str(), O_RDONLY );
	if( statfd == -1 ) {
		dprintf( D_ALWAYS, "FileModifiedTrigger( %s ): open() failed: %s (%d).\n", filename.c_str(), strerror(errno), errno );
		return;
	}

#if defined( LINUX )
#if defined( IN_NONBLOCK )
	inotify_fd = inotify_init1( IN_NONBLOCK );
#else
	inotify_fd = inotify_init();
	int flags = fcntl(inotify_fd, F_GETFL, 0);
	fcntl(inotify_fd, F_SETFL, flags | O_NONBLOCK);
#endif /* defined( IN_NONBLOCK ) */
	if( inotify_fd == -1 ) {
		dprintf( D_ALWAYS, "FileModifiedTrigger( %s ): inotify_init() failed: %s (%d).\n", filename.c_str(), strerror(errno), errno );
		return;
	}

	int wd = inotify_add_watch( inotify_fd, filename.c_str(), IN_MODIFY );
	if( wd == -1 ) {
		dprintf( D_ALWAYS, "FileModifiedTrigger( %s ): inotify_add_watch() failed: %s (%d).\n", filename.c_str(), strerror( errno ), errno );
		return;
	}
#endif /* defined( LINUX ) */

	initialized = true;
}

FileModifiedTrigger::~FileModifiedTrigger() {
	if( initialized && statfd != -1 ) {
		close( statfd );
		statfd = -1;
	}

#if defined( LINUX )
	if( initialized && inotify_fd != -1 ) {
		close( inotify_fd );
		inotify_fd = -1;
	}
#endif /* defined( LINUX ) */
}

//
// We would like to support the Linux and Windows -specific nonpolling
// solutions for detecting changes to the log file, but inotify blocks
// forever on network filesystems, and there's no Linux API which will
// let us know that ahead of time.  Instead, we use a polling approach
// on all platforms, but instead of sleeping on some platforms, we use
// inotify with the sleep duration as its timeout.
//
// On Linux, this means that notify_or_sleep() "sleeps" in poll(), and
// if the inotify fd goes hot, uses read_inotify_events() to clear the
// fd for the next time.
//

#if defined( LINUX )

int
FileModifiedTrigger::read_inotify_events( void ) {
	// Magic from 'man inotify'.
	char buf[ sizeof(struct inotify_event) + NAME_MAX + 1 ]
		__attribute__ ((aligned(__alignof__(struct inotify_event))));

	while( true ) {
		ssize_t len = read( inotify_fd, buf, sizeof( buf ) );
		if( len == -1 && errno != EAGAIN ) {
			dprintf( D_ALWAYS, "FileModifiedTrigger::read_inotify_events(%s): failed to ready from inotify fd.\n", filename.c_str() );
			return -1;
		}

		// We're done reading events for now.
		if( len <= 0 ) { return 1; }

		char * ptr = buf;
		for( ; ptr < buf + len; ptr += sizeof(struct inotify_event) + ((struct inotify_event *)ptr)->len ) {
			const struct inotify_event * event = (struct inotify_event *)ptr;
			if(! (event->mask & IN_MODIFY) ) {
				dprintf( D_ALWAYS, "FileModifiedTrigger::read_inotify_events(%s): inotify gave me an event I didn't ask for.\n", filename.c_str() );
				return -1;
			}
		}

		// We don't worry about partial reads because we're only watching
		// one file for one event type, and the kernel will coalesce
		// identical events, so we should only ever see one. Nonetheless,
		// we'll verify here that we read only complete events.
		if( ptr != buf + len ) {
			dprintf( D_ALWAYS, "FileModifiedTrigger::read_inotify_events(%s): partial inotify read.\n", filename.c_str() );
			return -1;
		}
	}

	return 1;
}

int
FileModifiedTrigger::notify_or_sleep( int waitfor ) {
	struct pollfd pollfds[1];
	pollfds[0].fd = inotify_fd;
	pollfds[0].events = POLLIN;
	pollfds[0].revents = 0;

	int events = poll( pollfds, 1, waitfor );
	switch( events ) {
		case -1:
			return -1;

		case 0:
			return 0;

		default:
			if( pollfds[0].revents & POLLIN ) {
				return read_inotify_events();
			} else {
				dprintf( D_ALWAYS, "FileModifiedTrigger::wait(): inotify returned an event I didn't ask for.\n" );
				return -1;
			}
	}
}

#else

#if defined(WINDOWS)
int usleep( long long microseconds ) {
	HANDLE timer;
	LARGE_INTEGER timerIntervals = { microseconds * -10 };
	timer = CreateWaitableTimer( NULL, true, NULL );
	SetWaitableTimer( timer, & timerIntervals, 0, NULL, NULL, false );
	WaitForSingleObject( timer, INFINITE );
	CloseHandle( timer );
	return 0;
}
#endif /* defined(WINDOWS) */

int
FileModifiedTrigger::notify_or_sleep( int waitfor ) {
	return usleep( waitfor * 1000 );
}

#endif /* defined( LINUX ) */

int
FileModifiedTrigger::wait( int timeout ) {
	if(! initialized) {
		return -1;
	}

	struct timeval deadline;
	condor_gettimestamp( deadline );

	deadline.tv_sec += timeout / 1000;
	deadline.tv_usec += (timeout % 1000) * 1000;
	deadline.tv_usec = deadline.tv_usec % 1000000;

	while( true ) {
		struct stat statbuf;
		int rv = fstat( statfd, & statbuf );
		if( rv != 0 ) {
			dprintf( D_ALWAYS, "FileModifiedTrigger::wait(): fstat() failure on previously-valid fd: %s (%d).\n", strerror(errno), errno );
			return -1;
		}

		bool changed = statbuf.st_size != lastSize;
		lastSize = statbuf.st_size;
		if( changed ) { return 1; }

		struct timeval now;
		condor_gettimestamp( now );

		if( deadline.tv_sec < now.tv_sec ) { return 0; }
		else if( deadline.tv_sec == now.tv_sec &&
			deadline.tv_usec < now.tv_usec ) { return 0; }

		int waitfor = ((deadline.tv_sec - now.tv_sec) * 1000) +
						((deadline.tv_usec - now.tv_usec) / 1000);
		if( waitfor > 5000 ) { waitfor = 5000; }

		int events = notify_or_sleep( waitfor );
		if( events == 1 ) { return 1; }
		if( events == 0 ) { continue; }
		return -1;
	}

}
<|endoftext|>


<commit_before>/*
 * LintillaMmiScreenState.cpp
 *
 *  Created on: 15.09.2014
 *      Author: niklausd
 */

#include "Arduino.h"

#include <LintillaMmi.h>
#include <LintillaMmiScreenFsm.h>
#include <LintillaMmiScreenState.h>
#include <LintillaMmiScreen.h>

LintillaMmiScreenState::LintillaMmiScreenState()
: m_screen(0)
{ }

LintillaMmiScreenState::~LintillaMmiScreenState()
{ }

void LintillaMmiScreenState::updateDisplay()
{
  screen()->updateDisplay();
}

//-----------------------------------------------------------------------------

LintillaMmiScreenState* LintillaMmiHomeScreenState::s_instance = 0;

LintillaMmiScreenState* LintillaMmiHomeScreenState::Instance()
{
  if (0 == s_instance)
  {
    s_instance = new LintillaMmiHomeScreenState();
  }
  return s_instance;
}

const char* LintillaMmiHomeScreenState::toString()
{
  return "HomeScreenState";
}

void LintillaMmiHomeScreenState::select(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()) && (0 != screen()->mmi()) && (0 != screen()->mmi()->adapter()))
  {
    if (!screen()->mmi()->adapter()->isSeqRunning())
    {
      fsm->changeState(LintillaMmiIdScreenState::Instance());
    }
  }
}

void LintillaMmiHomeScreenState::left(LintillaMmiScreenFsm* fsm)
{
  if ((0 != screen()) && (0 != screen()->mmi()) && (0 != screen()->mmi()->adapter()))
  {
    if (!screen()->mmi()->adapter()->isSeqRunning())
    {
      screen()->mmi()->adapter()->startSequence();
    }
  }
}

void LintillaMmiHomeScreenState::right(LintillaMmiScreenFsm* fsm)
{
  if ((0 != screen()) && (0 != screen()->mmi()) && (0 != screen()->mmi()->adapter()))
  {
    screen()->mmi()->adapter()->stopSequence();
  }
}

void LintillaMmiHomeScreenState::up(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorUp();
  }
}

void LintillaMmiHomeScreenState::down(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorDown();
  }
}

void LintillaMmiHomeScreenState::entry(LintillaMmiScreenFsm* fsm)
{
  Serial.println(toString());
  updateDisplay();
}

//-----------------------------------------------------------------------------

LintillaMmiScreenState* LintillaMmiIdScreenState::s_instance = 0;

LintillaMmiScreenState* LintillaMmiIdScreenState::Instance()
{
  if (0 == s_instance)
  {
    s_instance = new LintillaMmiIdScreenState();
  }
  return s_instance;
}

const char* LintillaMmiIdScreenState::toString()
{
  return "IdScreenState";
}

void LintillaMmiIdScreenState::select(LintillaMmiScreenFsm* fsm)
{
  if ((0 != screen()) && (0 != fsm))
  {
    if (screen()->isEditMode())
    {
      screen()->setEditMode(false);
    }
    else
    {
      fsm->changeState(LintillaMmiWlanScreenState::Instance());
    }
  }
}

void LintillaMmiIdScreenState::left(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    if (!screen()->isEditMode())
    {
      screen()->setEditMode(true);
    }
  }
}

void LintillaMmiIdScreenState::right(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()))
  {
    if (!screen()->isEditMode())
    {
      fsm->changeState(LintillaMmiHomeScreenState::Instance());
    }
  }
}

void LintillaMmiIdScreenState::up(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorUp();
  }
}

void LintillaMmiIdScreenState::down(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorDown();
  }
}

void LintillaMmiIdScreenState::entry(LintillaMmiScreenFsm* fsm)
{
  Serial.println(toString());
  updateDisplay();
}

//-----------------------------------------------------------------------------

LintillaMmiScreenState* LintillaMmiWlanScreenState::s_instance = 0;

LintillaMmiScreenState* LintillaMmiWlanScreenState::Instance()
{
  if (0 == s_instance)
  {
    s_instance = new LintillaMmiWlanScreenState();
  }
  return s_instance;
}

const char* LintillaMmiWlanScreenState::toString()
{
  return "WlanScreenState";
}

void LintillaMmiWlanScreenState::select(LintillaMmiScreenFsm* fsm)
{
  if (screen()->isEditMode())
  {
    screen()->setEditMode(false);
  }
}

void LintillaMmiWlanScreenState::left(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    if (!screen()->isEditMode())
    {
      screen()->setEditMode(true);
    }
  }
}

void LintillaMmiWlanScreenState::right(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()))
  {
    if (!screen()->isEditMode())
    {
      fsm->changeState(LintillaMmiHomeScreenState::Instance());
    }
  }
}

void LintillaMmiWlanScreenState::up(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorUp();
  }
}

void LintillaMmiWlanScreenState::down(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorDown();
  }
}

void LintillaMmiWlanScreenState::entry(LintillaMmiScreenFsm* fsm)
{
  Serial.println(toString());
  updateDisplay();
}
<commit_msg>improve the usability of the wlan screen<commit_after>/*
 * LintillaMmiScreenState.cpp
 *
 *  Created on: 15.09.2014
 *      Author: niklausd
 */

#include "Arduino.h"

#include <LintillaMmi.h>
#include <LintillaMmiScreenFsm.h>
#include <LintillaMmiScreenState.h>
#include <LintillaMmiScreen.h>

LintillaMmiScreenState::LintillaMmiScreenState()
: m_screen(0)
{ }

LintillaMmiScreenState::~LintillaMmiScreenState()
{ }

void LintillaMmiScreenState::updateDisplay()
{
  screen()->updateDisplay();
}

//-----------------------------------------------------------------------------

LintillaMmiScreenState* LintillaMmiHomeScreenState::s_instance = 0;

LintillaMmiScreenState* LintillaMmiHomeScreenState::Instance()
{
  if (0 == s_instance)
  {
    s_instance = new LintillaMmiHomeScreenState();
  }
  return s_instance;
}

const char* LintillaMmiHomeScreenState::toString()
{
  return "HomeScreenState";
}

void LintillaMmiHomeScreenState::select(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()) && (0 != screen()->mmi()) && (0 != screen()->mmi()->adapter()))
  {
    if (!screen()->mmi()->adapter()->isSeqRunning())
    {
      fsm->changeState(LintillaMmiIdScreenState::Instance());
    }
  }
}

void LintillaMmiHomeScreenState::left(LintillaMmiScreenFsm* fsm)
{
  if ((0 != screen()) && (0 != screen()->mmi()) && (0 != screen()->mmi()->adapter()))
  {
    if (!screen()->mmi()->adapter()->isSeqRunning())
    {
      screen()->mmi()->adapter()->startSequence();
    }
  }
}

void LintillaMmiHomeScreenState::right(LintillaMmiScreenFsm* fsm)
{
  if ((0 != screen()) && (0 != screen()->mmi()) && (0 != screen()->mmi()->adapter()))
  {
    screen()->mmi()->adapter()->stopSequence();
  }
}

void LintillaMmiHomeScreenState::up(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorUp();
  }
}

void LintillaMmiHomeScreenState::down(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorDown();
  }
}

void LintillaMmiHomeScreenState::entry(LintillaMmiScreenFsm* fsm)
{
  Serial.println(toString());
  updateDisplay();
}

//-----------------------------------------------------------------------------

LintillaMmiScreenState* LintillaMmiIdScreenState::s_instance = 0;

LintillaMmiScreenState* LintillaMmiIdScreenState::Instance()
{
  if (0 == s_instance)
  {
    s_instance = new LintillaMmiIdScreenState();
  }
  return s_instance;
}

const char* LintillaMmiIdScreenState::toString()
{
  return "IdScreenState";
}

void LintillaMmiIdScreenState::select(LintillaMmiScreenFsm* fsm)
{
  if ((0 != screen()) && (0 != fsm))
  {
    if (screen()->isEditMode())
    {
      screen()->setEditMode(false);
    }
    else
    {
      fsm->changeState(LintillaMmiWlanScreenState::Instance());
    }
  }
}

void LintillaMmiIdScreenState::left(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    if (!screen()->isEditMode())
    {
      screen()->setEditMode(true);
    }
  }
}

void LintillaMmiIdScreenState::right(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()))
  {
    if (!screen()->isEditMode())
    {
      fsm->changeState(LintillaMmiHomeScreenState::Instance());
    }
  }
}

void LintillaMmiIdScreenState::up(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorUp();
  }
}

void LintillaMmiIdScreenState::down(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorDown();
  }
}

void LintillaMmiIdScreenState::entry(LintillaMmiScreenFsm* fsm)
{
  Serial.println(toString());
  updateDisplay();
}

//-----------------------------------------------------------------------------

LintillaMmiScreenState* LintillaMmiWlanScreenState::s_instance = 0;

LintillaMmiScreenState* LintillaMmiWlanScreenState::Instance()
{
  if (0 == s_instance)
  {
    s_instance = new LintillaMmiWlanScreenState();
  }
  return s_instance;
}

const char* LintillaMmiWlanScreenState::toString()
{
  return "WlanScreenState";
}

void LintillaMmiWlanScreenState::select(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()))
  {
    if (!screen()->isEditMode())
    {
      fsm->changeState(LintillaMmiHomeScreenState::Instance());
    }
  }
}

void LintillaMmiWlanScreenState::left(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    if (!screen()->isEditMode())
    {
      screen()->setEditMode(true);
    }
    else
    {
      screen()->setEditMode(false);
    }
  }
}

void LintillaMmiWlanScreenState::right(LintillaMmiScreenFsm* fsm)
{
  if ((0 != fsm) && (0 != screen()))
  {
    if (!screen()->isEditMode())
    {
      fsm->changeState(LintillaMmiHomeScreenState::Instance());
    }
  }
}

void LintillaMmiWlanScreenState::up(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorUp();
  }
}

void LintillaMmiWlanScreenState::down(LintillaMmiScreenFsm* fsm)
{
  if (0 != screen())
  {
    screen()->setCursorDown();
  }
}

void LintillaMmiWlanScreenState::entry(LintillaMmiScreenFsm* fsm)
{
  Serial.println(toString());
  updateDisplay();
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include "node_pointer.h"

#include <nn.h>
#include <pubsub.h>
#include <pipeline.h>
#include <bus.h>
#include <pair.h>
#include <reqrep.h>
#include <survey.h>
#include <inproc.h>
#include <ipc.h>
#include <tcp.h>

using v8::Array;
using v8::Function;
using v8::FunctionTemplate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;

NAN_METHOD(Socket) {
  int domain = Nan::To<int>(info[0]).FromJust();
  int protocol = Nan::To<int>(info[1]).FromJust();

  info.GetReturnValue().Set(Nan::New<Number>(nn_socket(domain, protocol)));
}

NAN_METHOD(Close) {
  int s = Nan::To<int>(info[0]).FromJust();
  int rc = 0;

  do {
    rc = nn_close(s);
  } while (rc < 0 && errno == EINTR);

  info.GetReturnValue().Set(Nan::New<Number>(rc));
}

NAN_METHOD(Setopt) {
  int s = Nan::To<int>(info[0]).FromJust();
  int level = Nan::To<int>(info[1]).FromJust();
  int option = Nan::To<int>(info[2]).FromJust();
  int optval = Nan::To<int>(info[3]).FromJust();

  info.GetReturnValue().Set(Nan::New<Number>(
      nn_setsockopt(s, level, option, &optval, sizeof(optval))));
}

NAN_METHOD(Getopt) {
  int s = Nan::To<int>(info[0]).FromJust();
  int level = Nan::To<int>(info[1]).FromJust();
  int option = Nan::To<int>(info[2]).FromJust();
  int optval;
  size_t optsize = sizeof(optval);

  // check if the function succeeds
  if (nn_getsockopt(s, level, option, &optval, &optsize) == 0) {
    info.GetReturnValue().Set(Nan::New<Number>(optval));
  }
}

NAN_METHOD(Chan) {
  int s = Nan::To<int>(info[0]).FromJust();
  int level = NN_SUB;
  int option = Nan::To<int>(info[1]).FromJust();
  v8::String::Utf8Value str(info[2]);

  info.GetReturnValue().Set(
      Nan::New<Number>(nn_setsockopt(s, level, option, *str, str.length())));
}

NAN_METHOD(Bind) {
  int s = Nan::To<int>(info[0]).FromJust();
  String::Utf8Value addr(info[1]);

  info.GetReturnValue().Set(Nan::New<Number>(nn_bind(s, *addr)));
}

NAN_METHOD(Connect) {
  int s = Nan::To<int>(info[0]).FromJust();
  String::Utf8Value addr(info[1]);

  info.GetReturnValue().Set(Nan::New<Number>(nn_connect(s, *addr)));
}

NAN_METHOD(Shutdown) {
  int s = Nan::To<int>(info[0]).FromJust();
  int how = Nan::To<int>(info[1]).FromJust();

  info.GetReturnValue().Set(Nan::New<Number>(nn_shutdown(s, how)));
}

NAN_METHOD(Send) {
  int s = Nan::To<int>(info[0]).FromJust();
  int flags = Nan::To<int>(info[2]).FromJust();

  if (node::Buffer::HasInstance(info[1])) {
    info.GetReturnValue().Set(Nan::New<Number>(nn_send(
        s, node::Buffer::Data(info[1]), node::Buffer::Length(info[1]), flags)));
  } else {
    v8::String::Utf8Value str(info[1]);
    info.GetReturnValue().Set(
        Nan::New<Number>(nn_send(s, *str, str.length(), flags)));
  }
}

NAN_METHOD(Recv) {
  int s = Nan::To<int>(info[0]).FromJust();
  int flags = Nan::To<int>(info[1]).FromJust();

  // Invoke nanomsg function.
  char *buf = NULL;
  int len = nn_recv(s, &buf, NN_MSG, flags);

  if (len > -1) {
    v8::Local<v8::Object> h = Nan::NewBuffer(len).ToLocalChecked();
    memcpy(node::Buffer::Data(h), buf, len);

    // don't leak memory
    nn_freemsg(buf);

    info.GetReturnValue().Set(h);
  } else {
    info.GetReturnValue().Set(Nan::New<Number>(len));
  }
}

NAN_METHOD(SymbolInfo) {
  int s = Nan::To<int>(info[0]).FromJust();
  struct nn_symbol_properties prop;
  int ret = nn_symbol_info(s, &prop, sizeof(prop));

  if (ret > 0) {
    Local<Object> obj = Nan::New<Object>();
    Nan::Set(obj, Nan::New("value").ToLocalChecked(),
             Nan::New<Number>(prop.value));
    Nan::Set(obj, Nan::New("ns").ToLocalChecked(), Nan::New<Number>(prop.ns));
    Nan::Set(obj, Nan::New("type").ToLocalChecked(),
             Nan::New<Number>(prop.type));
    Nan::Set(obj, Nan::New("unit").ToLocalChecked(),
             Nan::New<Number>(prop.unit));
    Nan::Set(obj, Nan::New("name").ToLocalChecked(),
             Nan::New<String>(prop.name).ToLocalChecked());
    info.GetReturnValue().Set(obj);
  } else if (ret != 0) {
    Nan::ThrowError(nn_strerror(nn_errno()));
  }
}

NAN_METHOD(Symbol) {
  int s = Nan::To<int>(info[0]).FromJust();
  int val;
  const char *ret = nn_symbol(s, &val);

  if (ret) {
    Local<Object> obj = Nan::New<Object>();
    Nan::Set(obj, Nan::New("value").ToLocalChecked(), Nan::New<Number>(val));
    Nan::Set(obj, Nan::New("name").ToLocalChecked(),
             Nan::New<String>(ret).ToLocalChecked());
    info.GetReturnValue().Set(obj);
  } else {
    // symbol index out of range
    // this behaviour seems inconsistent with SymbolInfo() above
    // but we are faithfully following the libnanomsg API, warta and all
    Nan::ThrowError(nn_strerror(nn_errno())); // EINVAL
  }
}

NAN_METHOD(Term) { nn_term(); }

// Pass in two sockets, or (socket, -1) or (-1, socket) for loopback
NAN_METHOD(Device) {
  int s1 = Nan::To<int>(info[0]).FromJust();
  int s2 = Nan::To<int>(info[1]).FromJust();

  // nn_device only returns when it encounters an error
  nn_device(s1, s2);
  Nan::ThrowError(nn_strerror(nn_errno()));
}

NAN_METHOD(Errno) { info.GetReturnValue().Set(Nan::New<Number>(nn_errno())); }

NAN_METHOD(Err) {
  info.GetReturnValue().Set(Nan::New(nn_strerror(nn_errno())).ToLocalChecked());
}

typedef struct nanomsg_socket_s {
  uv_poll_t poll_handle;
  uv_os_sock_t sockfd;
  Nan::Callback *callback;
} nanomsg_socket_t;

void NanomsgReadable(uv_poll_t *req, int status, int events) {
  Nan::HandleScope scope;

  nanomsg_socket_t *context;
  context = reinterpret_cast<nanomsg_socket_t *>(req);

  if (events & UV_READABLE) {
    Local<Value> argv[] = { Nan::New<Number>(events) };
    context->callback->Call(1, argv);
  }
}

NAN_METHOD(PollSendSocket) {
  int s = Nan::To<int>(info[0]).FromJust();
  Nan::Callback *callback = new Nan::Callback(info[1].As<Function>());

  nanomsg_socket_t *context;
  size_t siz = sizeof(uv_os_sock_t);

  context = reinterpret_cast<nanomsg_socket_t *>(calloc(1, sizeof *context));
  context->poll_handle.data = context;
  context->callback = callback;
  nn_getsockopt(s, NN_SOL_SOCKET, NN_SNDFD, &context->sockfd, &siz);

  if (context->sockfd != 0) {
    uv_poll_init_socket(uv_default_loop(), &context->poll_handle,
                        context->sockfd);
    uv_poll_start(&context->poll_handle, UV_READABLE, NanomsgReadable);
    info.GetReturnValue().Set(WrapPointer(context, 8));
  }
}

NAN_METHOD(PollReceiveSocket) {
  int s = Nan::To<int>(info[0]).FromJust();
  Nan::Callback *callback = new Nan::Callback(info[1].As<Function>());

  nanomsg_socket_t *context;
  size_t siz = sizeof(uv_os_sock_t);

  context = reinterpret_cast<nanomsg_socket_t *>(calloc(1, sizeof *context));
  context->poll_handle.data = context;
  context->callback = callback;
  nn_getsockopt(s, NN_SOL_SOCKET, NN_RCVFD, &context->sockfd, &siz);

  if (context->sockfd != 0) {
    uv_poll_init_socket(uv_default_loop(), &context->poll_handle,
                        context->sockfd);
    uv_poll_start(&context->poll_handle, UV_READABLE, NanomsgReadable);
    info.GetReturnValue().Set(WrapPointer(context, 8));
  }
}

NAN_METHOD(PollStop) {
  nanomsg_socket_t *context = UnwrapPointer<nanomsg_socket_t *>(info[0]);
  int r = uv_poll_stop(&context->poll_handle);
  info.GetReturnValue().Set(Nan::New<Number>(r));
}

class NanomsgDeviceWorker : public Nan::AsyncWorker {
public:
  NanomsgDeviceWorker(Nan::Callback *callback, int s1, int s2)
      : Nan::AsyncWorker(callback), s1(s1), s2(s2) {}
  ~NanomsgDeviceWorker() {}

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute() {
    // nn_errno() only returns on error
    nn_device(s1, s2);
    err = nn_errno();
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback() {
    Nan::HandleScope scope;

    Local<Value> argv[] = { Nan::New<Number>(err) };

    callback->Call(1, argv);
  };

private:
  int s1;
  int s2;
  int err;
};

// Asynchronous access to the `nn_device()` function
NAN_METHOD(DeviceWorker) {
  int s1 = Nan::To<int>(info[0]).FromJust();
  int s2 = Nan::To<int>(info[1]).FromJust();
  Nan::Callback *callback = new Nan::Callback(info[2].As<Function>());

  Nan::AsyncQueueWorker(new NanomsgDeviceWorker(callback, s1, s2));
}

#define EXPORT_METHOD(C, S)                                                    \
  Nan::Set(C, Nan::New(#S).ToLocalChecked(),                                   \
           Nan::GetFunction(Nan::New<FunctionTemplate>(S)).ToLocalChecked());

NAN_MODULE_INIT(InitAll) {
  Nan::HandleScope scope;

  // Export functions.
  EXPORT_METHOD(target, Socket);
  EXPORT_METHOD(target, Close);
  EXPORT_METHOD(target, Chan);
  EXPORT_METHOD(target, Bind);
  EXPORT_METHOD(target, Connect);
  EXPORT_METHOD(target, Shutdown);
  EXPORT_METHOD(target, Send);
  EXPORT_METHOD(target, Recv);
  EXPORT_METHOD(target, Errno);
  EXPORT_METHOD(target, PollSendSocket);
  EXPORT_METHOD(target, PollReceiveSocket);
  EXPORT_METHOD(target, PollStop);
  EXPORT_METHOD(target, DeviceWorker);
  EXPORT_METHOD(target, SymbolInfo);
  EXPORT_METHOD(target, Symbol);
  EXPORT_METHOD(target, Term);

  EXPORT_METHOD(target, Getopt);
  EXPORT_METHOD(target, Setopt);
  EXPORT_METHOD(target, Err);

  // Export symbols.
  for (int i = 0;; ++i) {
    int value;
    const char *symbol_name = nn_symbol(i, &value);
    if (symbol_name == NULL) {
      break;
    }
    Nan::Set(target, Nan::New(symbol_name).ToLocalChecked(),
             Nan::New<Number>(value));
  }
}

NODE_MODULE(node_nanomsg, InitAll)
<commit_msg>Avoid copying received message buffers<commit_after>#include <stdio.h>
#include <stdlib.h>
#include "node_pointer.h"

#include <nn.h>
#include <pubsub.h>
#include <pipeline.h>
#include <bus.h>
#include <pair.h>
#include <reqrep.h>
#include <survey.h>
#include <inproc.h>
#include <ipc.h>
#include <tcp.h>

using v8::Array;
using v8::Function;
using v8::FunctionTemplate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;

NAN_METHOD(Socket) {
  int domain = Nan::To<int>(info[0]).FromJust();
  int protocol = Nan::To<int>(info[1]).FromJust();

  info.GetReturnValue().Set(Nan::New<Number>(nn_socket(domain, protocol)));
}

NAN_METHOD(Close) {
  int s = Nan::To<int>(info[0]).FromJust();
  int rc = 0;

  do {
    rc = nn_close(s);
  } while (rc < 0 && errno == EINTR);

  info.GetReturnValue().Set(Nan::New<Number>(rc));
}

NAN_METHOD(Setopt) {
  int s = Nan::To<int>(info[0]).FromJust();
  int level = Nan::To<int>(info[1]).FromJust();
  int option = Nan::To<int>(info[2]).FromJust();
  int optval = Nan::To<int>(info[3]).FromJust();

  info.GetReturnValue().Set(Nan::New<Number>(
      nn_setsockopt(s, level, option, &optval, sizeof(optval))));
}

NAN_METHOD(Getopt) {
  int s = Nan::To<int>(info[0]).FromJust();
  int level = Nan::To<int>(info[1]).FromJust();
  int option = Nan::To<int>(info[2]).FromJust();
  int optval;
  size_t optsize = sizeof(optval);

  // check if the function succeeds
  if (nn_getsockopt(s, level, option, &optval, &optsize) == 0) {
    info.GetReturnValue().Set(Nan::New<Number>(optval));
  }
}

NAN_METHOD(Chan) {
  int s = Nan::To<int>(info[0]).FromJust();
  int level = NN_SUB;
  int option = Nan::To<int>(info[1]).FromJust();
  v8::String::Utf8Value str(info[2]);

  info.GetReturnValue().Set(
      Nan::New<Number>(nn_setsockopt(s, level, option, *str, str.length())));
}

NAN_METHOD(Bind) {
  int s = Nan::To<int>(info[0]).FromJust();
  String::Utf8Value addr(info[1]);

  info.GetReturnValue().Set(Nan::New<Number>(nn_bind(s, *addr)));
}

NAN_METHOD(Connect) {
  int s = Nan::To<int>(info[0]).FromJust();
  String::Utf8Value addr(info[1]);

  info.GetReturnValue().Set(Nan::New<Number>(nn_connect(s, *addr)));
}

NAN_METHOD(Shutdown) {
  int s = Nan::To<int>(info[0]).FromJust();
  int how = Nan::To<int>(info[1]).FromJust();

  info.GetReturnValue().Set(Nan::New<Number>(nn_shutdown(s, how)));
}

NAN_METHOD(Send) {
  int s = Nan::To<int>(info[0]).FromJust();
  int flags = Nan::To<int>(info[2]).FromJust();

  if (node::Buffer::HasInstance(info[1])) {
    info.GetReturnValue().Set(Nan::New<Number>(nn_send(
        s, node::Buffer::Data(info[1]), node::Buffer::Length(info[1]), flags)));
  } else {
    v8::String::Utf8Value str(info[1]);
    info.GetReturnValue().Set(
        Nan::New<Number>(nn_send(s, *str, str.length(), flags)));
  }
}

void fcb(char *data, void *hint) {
  nn_freemsg(data);
  (void) hint;
}

NAN_METHOD(Recv) {
  int s = Nan::To<int>(info[0]).FromJust();
  int flags = Nan::To<int>(info[1]).FromJust();

  // Invoke nanomsg function.
  char *buf = NULL;
  int len = nn_recv(s, &buf, NN_MSG, flags);

  if (len > -1) {
    v8::Local<v8::Object> h = Nan::NewBuffer(buf, len, fcb, 0).ToLocalChecked();
    info.GetReturnValue().Set(h);
  } else {
    info.GetReturnValue().Set(Nan::New<Number>(len));
  }
}

NAN_METHOD(SymbolInfo) {
  int s = Nan::To<int>(info[0]).FromJust();
  struct nn_symbol_properties prop;
  int ret = nn_symbol_info(s, &prop, sizeof(prop));

  if (ret > 0) {
    Local<Object> obj = Nan::New<Object>();
    Nan::Set(obj, Nan::New("value").ToLocalChecked(),
             Nan::New<Number>(prop.value));
    Nan::Set(obj, Nan::New("ns").ToLocalChecked(), Nan::New<Number>(prop.ns));
    Nan::Set(obj, Nan::New("type").ToLocalChecked(),
             Nan::New<Number>(prop.type));
    Nan::Set(obj, Nan::New("unit").ToLocalChecked(),
             Nan::New<Number>(prop.unit));
    Nan::Set(obj, Nan::New("name").ToLocalChecked(),
             Nan::New<String>(prop.name).ToLocalChecked());
    info.GetReturnValue().Set(obj);
  } else if (ret != 0) {
    Nan::ThrowError(nn_strerror(nn_errno()));
  }
}

NAN_METHOD(Symbol) {
  int s = Nan::To<int>(info[0]).FromJust();
  int val;
  const char *ret = nn_symbol(s, &val);

  if (ret) {
    Local<Object> obj = Nan::New<Object>();
    Nan::Set(obj, Nan::New("value").ToLocalChecked(), Nan::New<Number>(val));
    Nan::Set(obj, Nan::New("name").ToLocalChecked(),
             Nan::New<String>(ret).ToLocalChecked());
    info.GetReturnValue().Set(obj);
  } else {
    // symbol index out of range
    // this behaviour seems inconsistent with SymbolInfo() above
    // but we are faithfully following the libnanomsg API, warta and all
    Nan::ThrowError(nn_strerror(nn_errno())); // EINVAL
  }
}

NAN_METHOD(Term) { nn_term(); }

// Pass in two sockets, or (socket, -1) or (-1, socket) for loopback
NAN_METHOD(Device) {
  int s1 = Nan::To<int>(info[0]).FromJust();
  int s2 = Nan::To<int>(info[1]).FromJust();

  // nn_device only returns when it encounters an error
  nn_device(s1, s2);
  Nan::ThrowError(nn_strerror(nn_errno()));
}

NAN_METHOD(Errno) { info.GetReturnValue().Set(Nan::New<Number>(nn_errno())); }

NAN_METHOD(Err) {
  info.GetReturnValue().Set(Nan::New(nn_strerror(nn_errno())).ToLocalChecked());
}

typedef struct nanomsg_socket_s {
  uv_poll_t poll_handle;
  uv_os_sock_t sockfd;
  Nan::Callback *callback;
} nanomsg_socket_t;

void NanomsgReadable(uv_poll_t *req, int status, int events) {
  Nan::HandleScope scope;

  nanomsg_socket_t *context;
  context = reinterpret_cast<nanomsg_socket_t *>(req);

  if (events & UV_READABLE) {
    Local<Value> argv[] = { Nan::New<Number>(events) };
    context->callback->Call(1, argv);
  }
}

NAN_METHOD(PollSendSocket) {
  int s = Nan::To<int>(info[0]).FromJust();
  Nan::Callback *callback = new Nan::Callback(info[1].As<Function>());

  nanomsg_socket_t *context;
  size_t siz = sizeof(uv_os_sock_t);

  context = reinterpret_cast<nanomsg_socket_t *>(calloc(1, sizeof *context));
  context->poll_handle.data = context;
  context->callback = callback;
  nn_getsockopt(s, NN_SOL_SOCKET, NN_SNDFD, &context->sockfd, &siz);

  if (context->sockfd != 0) {
    uv_poll_init_socket(uv_default_loop(), &context->poll_handle,
                        context->sockfd);
    uv_poll_start(&context->poll_handle, UV_READABLE, NanomsgReadable);
    info.GetReturnValue().Set(WrapPointer(context, 8));
  }
}

NAN_METHOD(PollReceiveSocket) {
  int s = Nan::To<int>(info[0]).FromJust();
  Nan::Callback *callback = new Nan::Callback(info[1].As<Function>());

  nanomsg_socket_t *context;
  size_t siz = sizeof(uv_os_sock_t);

  context = reinterpret_cast<nanomsg_socket_t *>(calloc(1, sizeof *context));
  context->poll_handle.data = context;
  context->callback = callback;
  nn_getsockopt(s, NN_SOL_SOCKET, NN_RCVFD, &context->sockfd, &siz);

  if (context->sockfd != 0) {
    uv_poll_init_socket(uv_default_loop(), &context->poll_handle,
                        context->sockfd);
    uv_poll_start(&context->poll_handle, UV_READABLE, NanomsgReadable);
    info.GetReturnValue().Set(WrapPointer(context, 8));
  }
}

NAN_METHOD(PollStop) {
  nanomsg_socket_t *context = UnwrapPointer<nanomsg_socket_t *>(info[0]);
  int r = uv_poll_stop(&context->poll_handle);
  info.GetReturnValue().Set(Nan::New<Number>(r));
}

class NanomsgDeviceWorker : public Nan::AsyncWorker {
public:
  NanomsgDeviceWorker(Nan::Callback *callback, int s1, int s2)
      : Nan::AsyncWorker(callback), s1(s1), s2(s2) {}
  ~NanomsgDeviceWorker() {}

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute() {
    // nn_errno() only returns on error
    nn_device(s1, s2);
    err = nn_errno();
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback() {
    Nan::HandleScope scope;

    Local<Value> argv[] = { Nan::New<Number>(err) };

    callback->Call(1, argv);
  };

private:
  int s1;
  int s2;
  int err;
};

// Asynchronous access to the `nn_device()` function
NAN_METHOD(DeviceWorker) {
  int s1 = Nan::To<int>(info[0]).FromJust();
  int s2 = Nan::To<int>(info[1]).FromJust();
  Nan::Callback *callback = new Nan::Callback(info[2].As<Function>());

  Nan::AsyncQueueWorker(new NanomsgDeviceWorker(callback, s1, s2));
}

#define EXPORT_METHOD(C, S)                                                    \
  Nan::Set(C, Nan::New(#S).ToLocalChecked(),                                   \
           Nan::GetFunction(Nan::New<FunctionTemplate>(S)).ToLocalChecked());

NAN_MODULE_INIT(InitAll) {
  Nan::HandleScope scope;

  // Export functions.
  EXPORT_METHOD(target, Socket);
  EXPORT_METHOD(target, Close);
  EXPORT_METHOD(target, Chan);
  EXPORT_METHOD(target, Bind);
  EXPORT_METHOD(target, Connect);
  EXPORT_METHOD(target, Shutdown);
  EXPORT_METHOD(target, Send);
  EXPORT_METHOD(target, Recv);
  EXPORT_METHOD(target, Errno);
  EXPORT_METHOD(target, PollSendSocket);
  EXPORT_METHOD(target, PollReceiveSocket);
  EXPORT_METHOD(target, PollStop);
  EXPORT_METHOD(target, DeviceWorker);
  EXPORT_METHOD(target, SymbolInfo);
  EXPORT_METHOD(target, Symbol);
  EXPORT_METHOD(target, Term);

  EXPORT_METHOD(target, Getopt);
  EXPORT_METHOD(target, Setopt);
  EXPORT_METHOD(target, Err);

  // Export symbols.
  for (int i = 0;; ++i) {
    int value;
    const char *symbol_name = nn_symbol(i, &value);
    if (symbol_name == NULL) {
      break;
    }
    Nan::Set(target, Nan::New(symbol_name).ToLocalChecked(),
             Nan::New<Number>(value));
  }
}

NODE_MODULE(node_nanomsg, InitAll)
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010, 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <algorithm>

#include <kernel/userver.hh>
#include <object/symbols.hh>
#include <runner/job.hh>
#include <urbi/object/event.hh>
#include <urbi/object/event-handler.hh>
#include <urbi/object/lobby.hh>
#include <urbi/sdk.hh>

#include <eval/call.hh>
#include <eval/exec.hh>
#include <eval/send-message.hh>

#include <urbi/runner/raise.hh>

GD_CATEGORY(Urbi.Event);

namespace urbi
{
  namespace object
  {

    /*---------.
    | Waiter.  |
    `---------*/

    inline
    Event::Waiter::Waiter(rTag ct, runner::Job* r, rObject& p)
      : controlTag(ct), runner(r), pattern(p)
    {}

    /*--------.
    | Event.  |
    `--------*/

    Event::Event()
      : waiters_()
      , onSubscribe_(0)
      , callbacks_()
    {
      proto_add(proto);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    Event::Event(rEvent model)
      : waiters_()
      , onSubscribe_(0)
      , callbacks_(model->callbacks_)
    {
      proto_add(model);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    URBI_CXX_OBJECT_INIT(Event)
      : waiters_()
      , onSubscribe_(0)
      , callbacks_()
    {
      BIND(hasSubscribers);
      BIND(onEvent, onEvent, on_event_type);
      BIND(onSubscribe, onSubscribe_);
      BIND(subscribe);
      BIND(subscribers, callbacks_);
      BIND(waituntil);
      BIND_VARIADIC(emit);
      BIND_VARIADIC(syncEmit);
      BIND_VARIADIC(syncTrigger);
      BIND_VARIADIC(trigger);
    }

    Event::~Event()
    {
      destructed_();
    }

    /*------------.
    | Callbacks.  |
    `------------*/

    Event::signal_type&
    Event::destructed_get()
    {
      return destructed_;
    }

    Event::signal_type&
    Event::subscribed_get()
    {
      return subscribed_;
    }

    Event::signal_type&
    Event::unsubscribed_get()
    {
      return unsubscribed_;
    }

    void
    Event::subscribe(rSubscription s)
    {
      aver(s);
      GD_FINFO_TRACE("%s: New subscription %s", this, s);
      callbacks_ << s;
      if (onSubscribe_)
        onSubscribe_->syncEmit();
    }

    rSubscription
    Event::onEvent(const callback_type& cb)
    {
      aver(!cb.empty());
      rSubscription res = new Subscription(new callback_type(cb));
      res->asynchronous_ = false;
      callbacks_ << res;
      if (onSubscribe_)
        onSubscribe_->syncEmit();
      return res;
    }

    /*-----------------.
    | Urbi functions.  |
    `-----------------*/

    void
    Event::onEvent(rExecutable guard, rExecutable enter, rExecutable leave,
                   bool sync)
    {
      rSubscription actions(new Subscription(this, guard, enter, leave, sync));
      GD_FPUSH_TRACE("%s: New registration %s.", this, actions);
      runner::Job& r = ::kernel::runner();
      actions->call_stack = r.state.call_stack_get();
      const libport::Symbol& sep =
        SYMBOL(MINUS_MINUS_MINUS_MINUS_SP_event_SP_handler_SP_backtrace_COLON);
      actions->call_stack << std::make_pair(sep, boost::optional<ast::loc>());

      actions->profile = r.profile_get();
      actions->tag_stack = r.state.tag_stack_get();
      actions->lobby = r.state.lobby_get();
      foreach (object::rTag& tag, actions->tag_stack)
      {
        GD_FINFO_DEBUG("%s: Hook tag %s.", this, tag->name());
        sched::rTag t = tag->value_get();
        using boost::bind;
        actions->connections
          << t->stop_hook_get().connect(
            bind(&Subscription::unregister, actions))
          << t->freeze_hook_get().connect(
            bind(&Subscription::freeze, actions))
          << t->unfreeze_hook_get().connect(
            bind(&Subscription::unfreeze, actions));
      }

      callbacks_ << actions;
      foreach (const actives_type::value_type& active, active_)
      {
        objects_type args;
        args << this << this << active->payload();
        rObject pattern = nil_class;
        if (actions->guard)
        {
          pattern = (*actions->guard)(args);
          if (pattern == void_class)
            continue;
        }
        args << pattern;
        if (actions->leave_)
          active
            ->register_stop_job
            (EventHandler::stop_job_type(actions, args, true));
        active->trigger_job(actions, true, args);
      }
      if (onSubscribe_)
        onSubscribe_->syncEmit();
      subscribed_();
    }

    void
    Event::waituntil(rObject pattern)
    {
      if (onSubscribe_)
        onSubscribe_->syncEmit();
      // Check whether there's already a matching instance.
      foreach (const actives_type::value_type& active, active_)
        if (pattern == nil_class
            || pattern->call(SYMBOL(match), active->payload())->as_bool())
          return;

      runner::Job& r = ::kernel::runner();
      rTag t(new Tag);
      waiters_ << Waiter(t, &r, pattern);
      libport::Finally f;
      r.state.apply_tag(t, &f);
      f << boost::bind(&Event::waituntil_remove, this, t);
      t->freeze(); // Will yield.
    }

    void
    Event::waituntil_remove(rTag what)
    {
      for (unsigned i = 0; i < waiters_.size(); ++i)
        if (waiters_[i].controlTag == what)
        {
          waiters_[i] = waiters_[waiters_.size()-1];
          waiters_.pop_back();
          return;
        }
    }

    rEvent
    Event::source()
    {
      rObject proto = protos_get_first();
      return from_urbi<rEvent>(proto);
    }

    /*-------.
    | Emit.  |
    `-------*/

    runner::rJob
    Event::spawn_actions_job(rLobby lobby, const call_stack_type& stack,
                             rExecutable e,
                             rProfile profile, const objects_type& args)
    {
      runner::Job& r = ::kernel::runner();
      if (!lobby)
        lobby = r.state.lobby_get();
      runner::Job* res =
        e->make_job(lobby, r.scheduler_get(), args, SYMBOL(event));
      // Append the back-trace of the event handler (the "at") below
      // that of the emission back trace.
      res->state
        .call_stack_get()
        .insert(res->state.call_stack_get().begin(),
                stack.begin(), stack.end());
      if (profile)
        res->profile_start(profile, SYMBOL(event), e.get());
      return res;
    }

    void
    Event::emit_backend(const objects_type& pl, bool detach, EventHandler* h)
    {
      GD_FPUSH_TRACE("%s: Emit, %s subscribers.", this, callbacks_.size());
      rList payload = new List(pl);
      if (!h)
        slot_update(SYMBOL(active), to_urbi(false));
      waituntil_release(payload);
      bool empty = callbacks_.empty();
      objects_type apl;
      callbacks_type cbcopy;
      // Copy active subscriptions, cleanup list
      for (unsigned i= 0; i<callbacks_.size(); ++i)
      {
        rSubscription& s = callbacks_[i];
        if (s->disconnected_get())
        {
          callbacks_[i] = callbacks_[callbacks_.size()-1];
          callbacks_.pop_back();
          --i;
        }
        else
          cbcopy << s;
      }
      // List was empty and we didn't notice.
      if (!empty && callbacks_.empty())
      {
        GD_INFO_TRACE("No more subscribers, calling unsubscribed");
        unsubscribed_();
      }
      GD_FINFO_TRACE("%s subscribers after cleanup", callbacks_.size());
      for (unsigned i = 0; i < cbcopy.size(); ++i)
      {
        rSubscription s = cbcopy[i];
        aver(s);
        libport::utime_t now = kernel::server().getTime();
        GD_FPUSH_TRACE
          ("Considering %s, mi %s(%s), lc %s(%s), now %s, enabl %s, async %s",
           s,
           s->minInterval_, libport::seconds_to_utime(s->minInterval_),
           s->lastCall_, libport::seconds_to_utime(s->lastCall_),
           now,
           s->enabled_,
           s->asynchronous_);
        if (s->disconnected_get())
        {
          GD_INFO_TRACE("Subscriber is disconnected");
          // For removal we just swap with the last for better performances.
          cbcopy[i] = cbcopy[cbcopy.size()-1];
          cbcopy.pop_back();
            --i;
        }
        else if (s->enabled_
                 && now - libport::seconds_to_utime(s->lastCall_) >=
                 libport::seconds_to_utime(s->minInterval_)
                 && (!s->maxParallelEvents_
                     || s->maxParallelEvents_ > s->processing_))
        {
          // FIXME CRAP if we honor the event emit sync/at sync rule, no way
          // to catch changed asynchronously
          bool async =
            (s->event_ && (detach && s->asynchronous_get()))
            || (!s->event_ && (detach || s->asynchronous_get()));
          GD_FINFO_TRACE("Subscriber is live for notification"
                         " (cb: %s e: %s), async: %s",
                         s->cb_, s->event_, async);
          if (s->frozen)
          {
            GD_FINFO_TRACE("%s: Skip frozen registration %s.", this, s);
            continue;
          }
          objects_type args;
          args << this << this << payload;
          rObject pattern = nil_class;
          if (s->guard)
          {
            pattern = (*s->guard)(args);
            if (pattern == void_class)
            {
              GD_FINFO_TRACE("%s: Skip patters mismatch %s.", this, s);
              continue;
            }
          }
          args << pattern;
          if (h && s->leave_)
            h->register_stop_job(EventHandler::stop_job_type(s, args, detach));
          if (async)
          {
            // If we create a job, it can die before executing a single line
            // of code.
            // To avoid any race condition, we just create the job without
            // touching any stat or holding any lock.
            eval::Action a =
              eval::exec(boost::bind(&Subscription::run_sync,
                                     s, this, pl, h, detach, false, args),
                         this);
            rLobby l =
              s->lobby ? s->lobby.get()
              : kernel::runner().state.lobby_get();
            runner::rJob j =
              new runner::Job(l, kernel::runner().scheduler_get());
            j->set_action(a);
            j->state.tag_stack_set(s->tag_stack);
            GD_FINFO_DUMP("Subscriber will run in job %s", j);
            j->start_job();
          }
          else
            s->run_sync(this, pl, h, detach, true, args);
        }
      }
    }

    void
    Event::syncEmit(const objects_type& pl)
    {
      emit_backend(pl, false);
    }

    void
    Event::emit(const objects_type& args)
    {
      emit_backend(args, true);
    }

    /*----------.
    | Trigger.  |
    `----------*/

    rEventHandler
    Event::trigger_backend(const objects_type& pl, bool detach)
    {
      rList payload = new List(pl);
      rEventHandler handler = new EventHandler(this, payload);
      waituntil_release(payload);
      handler->trigger(detach);
      return handler;
    }

    rEventHandler
    Event::syncTrigger(const objects_type& pl)
    {
      return trigger_backend(pl, false);
    }

    rEventHandler
    Event::trigger(const objects_type& pl)
    {
      return trigger_backend(pl, true);
    }

    void
    Event::waituntil_release(rObject payload)
    {
      // This iteration needs to remove some elements as it goes.
      for (unsigned i = 0; i < waiters_.size(); )
      {
	Waiter& waiter = waiters_[i];
	if (waiter.pattern == nil_class
            || waiter.pattern->call(SYMBOL(match), payload)->as_bool())
	{
          // Check if any tag is frozen beside the first one.
          bool frozen = false;
          foreach (const rTag& t, waiter.runner->state.tag_stack_get_all())
            if (t != waiter.controlTag && t->frozen())
            {
              frozen = true;
              ++i;
              break;
            }
          if (!frozen) // Do not trigger a frozen at.
          {
            waiter.controlTag->unfreeze();
            // Yes this is also valid for the last element.
            waiters_[i] = waiters_[waiters_.size()-1];
            waiters_.pop_back();
          }
	}
	else
	  ++i;
      }
    }

    bool
    Event::hasSubscribers() const
    {
      return !waiters_.empty() || !callbacks_.empty();
    }

  }
}
<commit_msg>Event: minor factoring.<commit_after>/*
 * Copyright (C) 2010, 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <algorithm>

#include <kernel/userver.hh>
#include <object/symbols.hh>
#include <runner/job.hh>
#include <urbi/object/event.hh>
#include <urbi/object/event-handler.hh>
#include <urbi/object/lobby.hh>
#include <urbi/sdk.hh>

#include <eval/call.hh>
#include <eval/exec.hh>
#include <eval/send-message.hh>

#include <urbi/runner/raise.hh>

GD_CATEGORY(Urbi.Event);

namespace urbi
{
  namespace object
  {

    /*---------.
    | Waiter.  |
    `---------*/

    inline
    Event::Waiter::Waiter(rTag ct, runner::Job* r, rObject& p)
      : controlTag(ct), runner(r), pattern(p)
    {}

    /*--------.
    | Event.  |
    `--------*/

    Event::Event()
      : waiters_()
      , onSubscribe_(0)
      , callbacks_()
    {
      proto_add(proto);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    Event::Event(rEvent model)
      : waiters_()
      , onSubscribe_(0)
      , callbacks_(model->callbacks_)
    {
      proto_add(model);
      slot_set_value(SYMBOL(active), to_urbi(false));
    }

    URBI_CXX_OBJECT_INIT(Event)
      : waiters_()
      , onSubscribe_(0)
      , callbacks_()
    {
      BIND(hasSubscribers);
      BIND(onEvent, onEvent, on_event_type);
      BIND(onSubscribe, onSubscribe_);
      BIND(subscribe);
      BIND(subscribers, callbacks_);
      BIND(waituntil);
      BIND_VARIADIC(emit);
      BIND_VARIADIC(syncEmit);
      BIND_VARIADIC(syncTrigger);
      BIND_VARIADIC(trigger);
    }

    Event::~Event()
    {
      destructed_();
    }

    /*------------.
    | Callbacks.  |
    `------------*/

    Event::signal_type&
    Event::destructed_get()
    {
      return destructed_;
    }

    Event::signal_type&
    Event::subscribed_get()
    {
      return subscribed_;
    }

    Event::signal_type&
    Event::unsubscribed_get()
    {
      return unsubscribed_;
    }

    void
    Event::subscribe(rSubscription s)
    {
      aver(s);
      GD_FINFO_TRACE("%s: New subscription %s", this, s);
      callbacks_ << s;
      if (onSubscribe_)
        onSubscribe_->syncEmit();
    }

    rSubscription
    Event::onEvent(const callback_type& cb)
    {
      aver(!cb.empty());
      rSubscription res = new Subscription(new callback_type(cb));
      res->asynchronous_ = false;
      callbacks_ << res;
      if (onSubscribe_)
        onSubscribe_->syncEmit();
      return res;
    }

    /*-----------------.
    | Urbi functions.  |
    `-----------------*/

    void
    Event::onEvent(rExecutable guard, rExecutable enter, rExecutable leave,
                   bool sync)
    {
      rSubscription actions(new Subscription(this, guard, enter, leave, sync));
      GD_FPUSH_TRACE("%s: New registration %s.", this, actions);
      runner::Job& r = ::kernel::runner();
      actions->call_stack = r.state.call_stack_get();
      const libport::Symbol& sep =
        SYMBOL(MINUS_MINUS_MINUS_MINUS_SP_event_SP_handler_SP_backtrace_COLON);
      actions->call_stack << std::make_pair(sep, boost::optional<ast::loc>());

      actions->profile = r.profile_get();
      actions->tag_stack = r.state.tag_stack_get();
      actions->lobby = r.state.lobby_get();
      foreach (object::rTag& tag, actions->tag_stack)
      {
        GD_FINFO_DEBUG("%s: Hook tag %s.", this, tag->name());
        sched::rTag t = tag->value_get();
        using boost::bind;
        actions->connections
          << t->stop_hook_get().connect(
            bind(&Subscription::unregister, actions))
          << t->freeze_hook_get().connect(
            bind(&Subscription::freeze, actions))
          << t->unfreeze_hook_get().connect(
            bind(&Subscription::unfreeze, actions));
      }

      subscribe(actions);
      foreach (const actives_type::value_type& active, active_)
      {
        objects_type args;
        args << this << this << active->payload();
        rObject pattern = nil_class;
        if (actions->guard)
        {
          pattern = (*actions->guard)(args);
          if (pattern == void_class)
            continue;
        }
        args << pattern;
        if (actions->leave_)
          active
            ->register_stop_job
            (EventHandler::stop_job_type(actions, args, true));
        active->trigger_job(actions, true, args);
      }
      subscribed_();
    }

    void
    Event::waituntil(rObject pattern)
    {
      if (onSubscribe_)
        onSubscribe_->syncEmit();
      // Check whether there's already a matching instance.
      foreach (const actives_type::value_type& active, active_)
        if (pattern == nil_class
            || pattern->call(SYMBOL(match), active->payload())->as_bool())
          return;

      runner::Job& r = ::kernel::runner();
      rTag t(new Tag);
      waiters_ << Waiter(t, &r, pattern);
      libport::Finally f;
      r.state.apply_tag(t, &f);
      f << boost::bind(&Event::waituntil_remove, this, t);
      t->freeze(); // Will yield.
    }

    void
    Event::waituntil_remove(rTag what)
    {
      for (unsigned i = 0; i < waiters_.size(); ++i)
        if (waiters_[i].controlTag == what)
        {
          waiters_[i] = waiters_[waiters_.size()-1];
          waiters_.pop_back();
          return;
        }
    }

    rEvent
    Event::source()
    {
      rObject proto = protos_get_first();
      return from_urbi<rEvent>(proto);
    }

    /*-------.
    | Emit.  |
    `-------*/

    runner::rJob
    Event::spawn_actions_job(rLobby lobby, const call_stack_type& stack,
                             rExecutable e,
                             rProfile profile, const objects_type& args)
    {
      runner::Job& r = ::kernel::runner();
      if (!lobby)
        lobby = r.state.lobby_get();
      runner::Job* res =
        e->make_job(lobby, r.scheduler_get(), args, SYMBOL(event));
      // Append the back-trace of the event handler (the "at") below
      // that of the emission back trace.
      res->state
        .call_stack_get()
        .insert(res->state.call_stack_get().begin(),
                stack.begin(), stack.end());
      if (profile)
        res->profile_start(profile, SYMBOL(event), e.get());
      return res;
    }

    void
    Event::emit_backend(const objects_type& pl, bool detach, EventHandler* h)
    {
      GD_FPUSH_TRACE("%s: Emit, %s subscribers.", this, callbacks_.size());
      rList payload = new List(pl);
      if (!h)
        slot_update(SYMBOL(active), to_urbi(false));
      waituntil_release(payload);
      bool empty = callbacks_.empty();
      objects_type apl;
      callbacks_type cbcopy;
      // Copy active subscriptions, cleanup list
      for (unsigned i= 0; i<callbacks_.size(); ++i)
      {
        rSubscription& s = callbacks_[i];
        if (s->disconnected_get())
        {
          callbacks_[i] = callbacks_[callbacks_.size()-1];
          callbacks_.pop_back();
          --i;
        }
        else
          cbcopy << s;
      }
      // List was empty and we didn't notice.
      if (!empty && callbacks_.empty())
      {
        GD_INFO_TRACE("No more subscribers, calling unsubscribed");
        unsubscribed_();
      }
      GD_FINFO_TRACE("%s subscribers after cleanup", callbacks_.size());
      for (unsigned i = 0; i < cbcopy.size(); ++i)
      {
        rSubscription s = cbcopy[i];
        aver(s);
        libport::utime_t now = kernel::server().getTime();
        GD_FPUSH_TRACE
          ("Considering %s, mi %s(%s), lc %s(%s), now %s, enabl %s, async %s",
           s,
           s->minInterval_, libport::seconds_to_utime(s->minInterval_),
           s->lastCall_, libport::seconds_to_utime(s->lastCall_),
           now,
           s->enabled_,
           s->asynchronous_);
        if (s->disconnected_get())
        {
          GD_INFO_TRACE("Subscriber is disconnected");
          // For removal we just swap with the last for better performances.
          cbcopy[i] = cbcopy[cbcopy.size()-1];
          cbcopy.pop_back();
            --i;
        }
        else if (s->enabled_
                 && now - libport::seconds_to_utime(s->lastCall_) >=
                 libport::seconds_to_utime(s->minInterval_)
                 && (!s->maxParallelEvents_
                     || s->maxParallelEvents_ > s->processing_))
        {
          // FIXME CRAP if we honor the event emit sync/at sync rule, no way
          // to catch changed asynchronously
          bool async =
            (s->event_ && (detach && s->asynchronous_get()))
            || (!s->event_ && (detach || s->asynchronous_get()));
          GD_FINFO_TRACE("Subscriber is live for notification"
                         " (cb: %s e: %s), async: %s",
                         s->cb_, s->event_, async);
          if (s->frozen)
          {
            GD_FINFO_TRACE("%s: Skip frozen registration %s.", this, s);
            continue;
          }
          objects_type args;
          args << this << this << payload;
          rObject pattern = nil_class;
          if (s->guard)
          {
            pattern = (*s->guard)(args);
            if (pattern == void_class)
            {
              GD_FINFO_TRACE("%s: Skip patters mismatch %s.", this, s);
              continue;
            }
          }
          args << pattern;
          if (h && s->leave_)
            h->register_stop_job(EventHandler::stop_job_type(s, args, detach));
          if (async)
          {
            // If we create a job, it can die before executing a single line
            // of code.
            // To avoid any race condition, we just create the job without
            // touching any stat or holding any lock.
            eval::Action a =
              eval::exec(boost::bind(&Subscription::run_sync,
                                     s, this, pl, h, detach, false, args),
                         this);
            rLobby l =
              s->lobby ? s->lobby.get()
              : kernel::runner().state.lobby_get();
            runner::rJob j =
              new runner::Job(l, kernel::runner().scheduler_get());
            j->set_action(a);
            j->state.tag_stack_set(s->tag_stack);
            GD_FINFO_DUMP("Subscriber will run in job %s", j);
            j->start_job();
          }
          else
            s->run_sync(this, pl, h, detach, true, args);
        }
      }
    }

    void
    Event::syncEmit(const objects_type& pl)
    {
      emit_backend(pl, false);
    }

    void
    Event::emit(const objects_type& args)
    {
      emit_backend(args, true);
    }

    /*----------.
    | Trigger.  |
    `----------*/

    rEventHandler
    Event::trigger_backend(const objects_type& pl, bool detach)
    {
      rList payload = new List(pl);
      rEventHandler handler = new EventHandler(this, payload);
      waituntil_release(payload);
      handler->trigger(detach);
      return handler;
    }

    rEventHandler
    Event::syncTrigger(const objects_type& pl)
    {
      return trigger_backend(pl, false);
    }

    rEventHandler
    Event::trigger(const objects_type& pl)
    {
      return trigger_backend(pl, true);
    }

    void
    Event::waituntil_release(rObject payload)
    {
      // This iteration needs to remove some elements as it goes.
      for (unsigned i = 0; i < waiters_.size(); )
      {
	Waiter& waiter = waiters_[i];
	if (waiter.pattern == nil_class
            || waiter.pattern->call(SYMBOL(match), payload)->as_bool())
	{
          // Check if any tag is frozen beside the first one.
          bool frozen = false;
          foreach (const rTag& t, waiter.runner->state.tag_stack_get_all())
            if (t != waiter.controlTag && t->frozen())
            {
              frozen = true;
              ++i;
              break;
            }
          if (!frozen) // Do not trigger a frozen at.
          {
            waiter.controlTag->unfreeze();
            // Yes this is also valid for the last element.
            waiters_[i] = waiters_[waiters_.size()-1];
            waiters_.pop_back();
          }
	}
	else
	  ++i;
      }
    }

    bool
    Event::hasSubscribers() const
    {
      return !waiters_.empty() || !callbacks_.empty();
    }

  }
}
<|endoftext|>