diff options
author | murphy <murphy@rubychan.de> | 2009-10-19 16:52:59 +0000 |
---|---|---|
committer | murphy <murphy@rubychan.de> | 2009-10-19 16:52:59 +0000 |
commit | a6f93eb4304a062af43083a75d525acc1af9883d (patch) | |
tree | 624996688e5b983a38ba08f9ea05a07017d0e432 | |
parent | 4f67b22339ea7025dc4536a7e369f60755103acc (diff) | |
download | coderay-a6f93eb4304a062af43083a75d525acc1af9883d.tar.gz |
New Scanner: *C++* (#76)!
There's a problem with the ternary operator (?:) and labels which
needs to be fixed in C, C++ and PHP scanners. I'll get to that soon.
-rw-r--r-- | lib/coderay/scanners/_map.rb | 4 | ||||
-rw-r--r-- | lib/coderay/scanners/cpp.rb | 197 | ||||
-rw-r--r-- | test/scanners/cpp/elvis.expected.raydebug | 1 | ||||
-rw-r--r-- | test/scanners/cpp/elvis.in.cpp | 1 | ||||
-rw-r--r-- | test/scanners/cpp/eventmachine.expected.raydebug | 7035 | ||||
-rw-r--r-- | test/scanners/cpp/eventmachine.in.cpp | 7035 | ||||
-rw-r--r-- | test/scanners/cpp/pleac.expected.raydebug | 2041 | ||||
-rw-r--r-- | test/scanners/cpp/pleac.in.cpp | 2041 | ||||
-rw-r--r-- | test/scanners/cpp/suite.rb | 2 | ||||
-rw-r--r-- | test/scanners/cpp/wedekind.expected.raydebug | 12 | ||||
-rw-r--r-- | test/scanners/cpp/wedekind.in.cpp | 12 |
11 files changed, 18380 insertions, 1 deletions
diff --git a/lib/coderay/scanners/_map.rb b/lib/coderay/scanners/_map.rb index 9f08d7d..01078c1 100644 --- a/lib/coderay/scanners/_map.rb +++ b/lib/coderay/scanners/_map.rb @@ -2,7 +2,9 @@ module CodeRay module Scanners map \ - :cpp => :c, + :h => :c, + :cplusplus => :cpp, + :'c++' => :cpp, :ecma => :java_script, :ecmascript => :java_script, :ecma_script => :java_script, diff --git a/lib/coderay/scanners/cpp.rb b/lib/coderay/scanners/cpp.rb new file mode 100644 index 0000000..7ba9ec0 --- /dev/null +++ b/lib/coderay/scanners/cpp.rb @@ -0,0 +1,197 @@ +module CodeRay +module Scanners + + class CPlusPlus < Scanner + + include Streamable + + register_for :cpp + file_extension 'cpp' + title 'C++' + + # http://www.cppreference.com/wiki/keywords/start + RESERVED_WORDS = [ + 'and', 'and_eq', 'asm', 'bitand', 'bitor', 'break', + 'case', 'catch', 'class', 'compl', 'const_cast', + 'continue', 'default', 'delete', 'do', 'dynamic_cast', 'else', + 'enum', 'export', 'for', 'goto', 'if', 'namespace', 'new', + 'not', 'not_eq', 'or', 'or_eq', 'reinterpret_cast', 'return', + 'sizeof', 'static_cast', 'struct', 'switch', 'template', + 'throw', 'try', 'typedef', 'typeid', 'typename', 'union', + 'while', 'xor', 'xor_eq' + ] + + PREDEFINED_TYPES = [ + 'bool', 'char', 'double', 'float', 'int', 'long', + 'short', 'signed', 'unsigned', 'wchar_t', 'string' + ] + PREDEFINED_CONSTANTS = [ + 'false', 'true', + 'EOF', 'NULL', + ] + PREDEFINED_VARIABLES = [ + 'this' + ] + DIRECTIVES = [ + 'auto', 'const', 'explicit', 'extern', 'friend', 'inline', 'mutable', 'operator', + 'private', 'protected', 'public', 'register', 'static', 'using', 'virtual', 'void', + 'volatile' + ] + + IDENT_KIND = WordList.new(:ident). + add(RESERVED_WORDS, :reserved). + add(PREDEFINED_TYPES, :pre_type). + add(PREDEFINED_VARIABLES, :local_variable). + add(DIRECTIVES, :directive). + add(PREDEFINED_CONSTANTS, :pre_constant) + + ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x + UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x + + def scan_tokens tokens, options + + state = :initial + + until eos? + + kind = nil + match = nil + + case state + + when :initial + + if scan(/ \s+ | \\\n /x) + kind = :space + + elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) + kind = :comment + + elsif match = scan(/ \# \s* if \s* 0 /x) + match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? + kind = :comment + + elsif scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x) + kind = :operator + + elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + kind = IDENT_KIND[match] + if kind == :ident and check(/:(?!:)/) + # FIXME: don't match a?b:c + kind = :label + elsif match == 'class' + state = :class_name_expected + end + + elsif scan(/\$/) + kind = :ident + + elsif match = scan(/L?"/) + tokens << [:open, :string] + if match[0] == ?L + tokens << ['L', :modifier] + match = '"' + end + state = :string + kind = :delimiter + + elsif scan(/#\s*(\w*)/) + kind = :preprocessor # FIXME multiline preprocs + state = :include_expected if self[1] == 'include' + + elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox) + kind = :char + + elsif scan(/0[xX][0-9A-Fa-f]+/) + kind = :hex + + elsif scan(/(?:0[0-7]+)(?![89.eEfF])/) + kind = :oct + + elsif scan(/(?:\d+)(?![.eEfF])L?L?/) + kind = :integer + + elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) + kind = :float + + else + getch + kind = :error + + end + + when :string + if scan(/[^\\"]+/) + kind = :content + elsif scan(/"/) + tokens << ['"', :delimiter] + tokens << [:close, :string] + state = :initial + next + elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) + kind = :char + elsif scan(/ \\ | $ /x) + tokens << [:close, :string] + kind = :error + state = :initial + else + raise_inspect "else case \" reached; %p not handled." % peek(1), tokens + end + + when :include_expected + if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) + kind = :include + state = :initial + + elsif match = scan(/\s+/) + kind = :space + state = :initial if match.index ?\n + + else + state = :initial + next + + end + + when :class_name_expected + if scan(/ [A-Za-z_][A-Za-z_0-9]* /x) + kind = :class + state = :initial + + elsif match = scan(/\s+/) + kind = :space + + else + getch + kind = :error + state = :initial + + end + + else + raise_inspect 'Unknown state', tokens + + end + + match ||= matched + if $DEBUG and not kind + raise_inspect 'Error token %p in line %d' % + [[match, kind], line], tokens + end + raise_inspect 'Empty token', tokens unless match + + tokens << [match, kind] + + end + + if state == :string + tokens << [:close, :string] + end + + tokens + end + + end + +end +end diff --git a/test/scanners/cpp/elvis.expected.raydebug b/test/scanners/cpp/elvis.expected.raydebug new file mode 100644 index 0000000..fdd1099 --- /dev/null +++ b/test/scanners/cpp/elvis.expected.raydebug @@ -0,0 +1 @@ +local_variable(this)operator(?)ident(is)operator(+)ident(no)operator(:)ident(label) diff --git a/test/scanners/cpp/elvis.in.cpp b/test/scanners/cpp/elvis.in.cpp new file mode 100644 index 0000000..e23f86e --- /dev/null +++ b/test/scanners/cpp/elvis.in.cpp @@ -0,0 +1 @@ +this?is+no:label diff --git a/test/scanners/cpp/eventmachine.expected.raydebug b/test/scanners/cpp/eventmachine.expected.raydebug new file mode 100644 index 0000000..82004cd --- /dev/null +++ b/test/scanners/cpp/eventmachine.expected.raydebug @@ -0,0 +1,7035 @@ +comment(/***************************************************************************** + +$Id$ + +File: binder.cpp +Date: 07Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + +preprocessor(#define) ident(DEV_URANDOM) string<delimiter(")content(/dev/urandom)delimiter(")> + + +ident(map)operator(<)pre_type(string)operator(,) ident(Bindable_t)operator(*>) ident(Bindable_t)operator(::)ident(BindingBag)operator(;) + + +comment(/******************************** +STATIC Bindable_t::CreateBinding +********************************/) + +pre_type(string) ident(Bindable_t)operator(::)ident(CreateBinding)operator((\)) +operator({) + directive(static) pre_type(int) ident(index) operator(=) integer(0)operator(;) + directive(static) pre_type(string) ident(seed)operator(;) + + reserved(if) operator((()ident(index) operator(>=) integer(1000000)operator(\)) operator(||) operator(()ident(seed)operator(.)ident(length)operator((\)) operator(==) integer(0)operator(\)\)) operator({) + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(fd) operator(=) ident(open) operator(()ident(DEV_URANDOM)operator(,) ident(O_RDONLY)operator(\);) + reserved(if) operator(()ident(fd) operator(<) integer(0)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(No entropy device)delimiter(")>operator(\);) + + pre_type(unsigned) pre_type(char) ident(u)operator([)integer(16)operator(];) + ident(size_t) ident(r) operator(=) ident(read) operator(()ident(fd)operator(,) ident(u)operator(,) reserved(sizeof)operator(()ident(u)operator(\)\);) + reserved(if) operator(()ident(r) operator(<) reserved(sizeof)operator(()ident(u)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(Unable to read entropy device)delimiter(")>operator(\);) + + pre_type(unsigned) pre_type(char) operator(*)ident(u1) operator(=) operator(()pre_type(unsigned) pre_type(char)operator(*\))ident(u)operator(;) + pre_type(char) ident(u2) operator([)reserved(sizeof)operator(()ident(u)operator(\)) operator(*) integer(2) operator(+) integer(1)operator(];) + + reserved(for) operator(()ident(size_t) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) reserved(sizeof)operator(()ident(u)operator(\);) ident(i)operator(++\)) + ident(sprintf) operator(()ident(u2) operator(+) operator(()ident(i) operator(*) integer(2)operator(\),) string<delimiter(")content(%02x)delimiter(")>operator(,) ident(u1)operator([)ident(i)operator(]\);) + + ident(seed) operator(=) pre_type(string) operator(()ident(u2)operator(\);) + preprocessor(#endif) + + + preprocessor(#ifdef) ident(OS_WIN32) + ident(UUID) ident(uuid)operator(;) + ident(UuidCreate) operator((&)ident(uuid)operator(\);) + pre_type(unsigned) pre_type(char) operator(*)ident(uuidstring) operator(=) pre_constant(NULL)operator(;) + ident(UuidToString) operator((&)ident(uuid)operator(,) operator(&)ident(uuidstring)operator(\);) + reserved(if) operator((!)ident(uuidstring)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(Unable to read uuid)delimiter(")>operator(\);) + ident(seed) operator(=) pre_type(string) operator((()directive(const) pre_type(char)operator(*\))ident(uuidstring)operator(\);) + + ident(RpcStringFree) operator((&)ident(uuidstring)operator(\);) + preprocessor(#endif) + + ident(index) operator(=) integer(0)operator(;) + + + operator(}) + + ident(stringstream) ident(ss)operator(;) + ident(ss) operator(<<) ident(seed) operator(<<) operator((++)ident(index)operator(\);) + reserved(return) ident(ss)operator(.)ident(str)operator((\);) +operator(}) + + +comment(/***************************** +STATIC: Bindable_t::GetObject +*****************************/) + +ident(Bindable_t) operator(*)ident(Bindable_t)operator(::)ident(GetObject) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + pre_type(string) ident(s) operator(()ident(binding) operator(?) ident(binding) operator(:) string<delimiter(")delimiter(")>operator(\);) + reserved(return) ident(GetObject) operator(()ident(s)operator(\);) +operator(}) + +comment(/***************************** +STATIC: Bindable_t::GetObject +*****************************/) + +ident(Bindable_t) operator(*)ident(Bindable_t)operator(::)ident(GetObject) operator(()directive(const) pre_type(string) operator(&)ident(binding)operator(\)) +operator({) + ident(map)operator(<)pre_type(string)operator(,) ident(Bindable_t)operator(*>::)ident(const_iterator) ident(i) operator(=) ident(BindingBag)operator(.)ident(find) operator(()ident(binding)operator(\);) + reserved(if) operator(()ident(i) operator(!=) ident(BindingBag)operator(.)ident(end)operator((\)\)) + reserved(return) ident(i)operator(->)ident(second)operator(;) + reserved(else) + reserved(return) pre_constant(NULL)operator(;) +operator(}) + + +comment(/********************** +Bindable_t::Bindable_t +**********************/) + +ident(Bindable_t)operator(::)ident(Bindable_t)operator((\)) +operator({) + ident(Binding) operator(=) ident(Bindable_t)operator(::)ident(CreateBinding)operator((\);) + ident(BindingBag) operator([)ident(Binding)operator(]) operator(=) local_variable(this)operator(;) +operator(}) + + + +comment(/*********************** +Bindable_t::~Bindable_t +***********************/) + +ident(Bindable_t)operator(::~)ident(Bindable_t)operator((\)) +operator({) + ident(BindingBag)operator(.)ident(erase) operator(()ident(Binding)operator(\);) +operator(}) + + +comment(/***************************************************************************** + +$Id$ + +File: cmain.cpp +Date: 06Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + + +directive(static) ident(EventMachine_t) operator(*)ident(EventMachine)operator(;) +directive(static) pre_type(int) ident(bUseEpoll) operator(=) integer(0)operator(;) +directive(static) pre_type(int) ident(bUseKqueue) operator(=) integer(0)operator(;) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(ensure_eventmachine) operator(()directive(const) pre_type(char) operator(*)ident(caller) operator(=) string<delimiter(")content(unknown caller)delimiter(")>operator(\)) +operator({) + reserved(if) operator((!)ident(EventMachine)operator(\)) operator({) + directive(const) pre_type(int) ident(err_size) operator(=) integer(128)operator(;) + pre_type(char) ident(err_string)operator([)ident(err_size)operator(];) + ident(snprintf) operator(()ident(err_string)operator(,) ident(err_size)operator(,) string<delimiter(")content(eventmachine not initialized: %s)delimiter(")>operator(,) ident(caller)operator(\);) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(rb_raise)operator(()ident(rb_eRuntimeError)operator(,) ident(err_string)operator(\);) + preprocessor(#else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(err_string)operator(\);) + preprocessor(#endif) + operator(}) +operator(}) + +comment(/*********************** +evma_initialize_library +***********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_initialize_library) operator(()directive(void)operator((*)ident(cb)operator(\)()directive(const) pre_type(char)operator(*,) pre_type(int)operator(,) directive(const) pre_type(char)operator(*,) pre_type(int)operator(\)\)) +operator({) + comment(// Probably a bad idea to mess with the signal mask of a process) + comment(// we're just being linked into.) + comment(//InstallSignalHandlers(\);) + reserved(if) operator(()ident(EventMachine)operator(\)) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(rb_raise)operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(eventmachine already initialized: evma_initialize_library)delimiter(")>operator(\);) + preprocessor(#else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(eventmachine already initialized: evma_initialize_library)delimiter(")>operator(\);) + preprocessor(#endif) + ident(EventMachine) operator(=) reserved(new) ident(EventMachine_t) operator(()ident(cb)operator(\);) + reserved(if) operator(()ident(bUseEpoll)operator(\)) + ident(EventMachine)operator(->)ident(_UseEpoll)operator((\);) + reserved(if) operator(()ident(bUseKqueue)operator(\)) + ident(EventMachine)operator(->)ident(_UseKqueue)operator((\);) +operator(}) + + +comment(/******************** +evma_release_library +********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_release_library)operator((\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_release_library)delimiter(")>operator(\);) + reserved(delete) ident(EventMachine)operator(;) + ident(EventMachine) operator(=) pre_constant(NULL)operator(;) +operator(}) + + +comment(/**************** +evma_run_machine +****************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_run_machine)operator((\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_run_machine)delimiter(")>operator(\);) + ident(EventMachine)operator(->)ident(Run)operator((\);) +operator(}) + + +comment(/************************** +evma_install_oneshot_timer +**************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_install_oneshot_timer) operator(()pre_type(int) ident(seconds)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_install_oneshot_timer)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(InstallOneshotTimer) operator(()ident(seconds)operator(\);) +operator(}) + + +comment(/********************** +evma_connect_to_server +**********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_connect_to_server) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_connect_to_server)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(ConnectToServer) operator(()ident(server)operator(,) ident(port)operator(\);) +operator(}) + +comment(/*************************** +evma_connect_to_unix_server +***************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_connect_to_unix_server) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_connect_to_unix_server)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(ConnectToUnixServer) operator(()ident(server)operator(\);) +operator(}) + +comment(/************** +evma_attach_fd +**************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_attach_fd) operator(()pre_type(int) ident(file_descriptor)operator(,) pre_type(int) ident(notify_readable)operator(,) pre_type(int) ident(notify_writable)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_attach_fd)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(AttachFD) operator(()ident(file_descriptor)operator(,) operator(()ident(notify_readable) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(\),) operator(()ident(notify_writable) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(\)\);) +operator(}) + +comment(/************** +evma_detach_fd +**************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_detach_fd) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_dettach_fd)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) + reserved(return) ident(EventMachine)operator(->)ident(DetachFD) operator(()ident(ed)operator(\);) + reserved(else) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(rb_raise)operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(invalid binding to detach)delimiter(")>operator(\);) + preprocessor(#else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(invalid binding to detach)delimiter(")>operator(\);) + preprocessor(#endif) +operator(}) + +comment(/********************** +evma_create_tcp_server +**********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_create_tcp_server) operator(()directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_create_tcp_server)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(CreateTcpServer) operator(()ident(address)operator(,) ident(port)operator(\);) +operator(}) + +comment(/****************************** +evma_create_unix_domain_server +******************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_create_unix_domain_server) operator(()directive(const) pre_type(char) operator(*)ident(filename)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_create_unix_domain_server)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(CreateUnixDomainServer) operator(()ident(filename)operator(\);) +operator(}) + +comment(/************************* +evma_open_datagram_socket +*************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_open_datagram_socket) operator(()directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_open_datagram_socket)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(OpenDatagramSocket) operator(()ident(address)operator(,) ident(port)operator(\);) +operator(}) + +comment(/****************** +evma_open_keyboard +******************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_open_keyboard)operator((\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_open_keyboard)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(OpenKeyboard)operator((\);) +operator(}) + + + +comment(/**************************** +evma_send_data_to_connection +****************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_send_data_to_connection) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(data_length)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_send_data_to_connection)delimiter(")>operator(\);) + reserved(return) ident(ConnectionDescriptor)operator(::)ident(SendDataToConnection) operator(()ident(binding)operator(,) ident(data)operator(,) ident(data_length)operator(\);) +operator(}) + +comment(/****************** +evma_send_datagram +******************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_send_datagram) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(data_length)operator(,) directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_send_datagram)delimiter(")>operator(\);) + reserved(return) ident(DatagramDescriptor)operator(::)ident(SendDatagram) operator(()ident(binding)operator(,) ident(data)operator(,) ident(data_length)operator(,) ident(address)operator(,) ident(port)operator(\);) +operator(}) + + +comment(/********************* +evma_close_connection +*********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_close_connection) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) pre_type(int) ident(after_writing)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_close_connection)delimiter(")>operator(\);) + ident(ConnectionDescriptor)operator(::)ident(CloseConnection) operator(()ident(binding)operator(,) operator(()ident(after_writing) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(\)\);) +operator(}) + +comment(/*********************************** +evma_report_connection_error_status +***********************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_report_connection_error_status) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_report_connection_error_status)delimiter(")>operator(\);) + reserved(return) ident(ConnectionDescriptor)operator(::)ident(ReportErrorStatus) operator(()ident(binding)operator(\);) +operator(}) + +comment(/******************** +evma_stop_tcp_server +********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_stop_tcp_server) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_stop_tcp_server)delimiter(")>operator(\);) + ident(AcceptorDescriptor)operator(::)ident(StopAcceptor) operator(()ident(binding)operator(\);) +operator(}) + + +comment(/***************** +evma_stop_machine +*****************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_stop_machine)operator((\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_stop_machine)delimiter(")>operator(\);) + ident(EventMachine)operator(->)ident(ScheduleHalt)operator((\);) +operator(}) + + +comment(/************** +evma_start_tls +**************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_start_tls) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_start_tls)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) + ident(ed)operator(->)ident(StartTls)operator((\);) +operator(}) + +comment(/****************** +evma_set_tls_parms +******************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_set_tls_parms) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) directive(const) pre_type(char) operator(*)ident(privatekey_filename)operator(,) directive(const) pre_type(char) operator(*)ident(certchain_filename)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_set_tls_parms)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) + ident(ed)operator(->)ident(SetTlsParms) operator(()ident(privatekey_filename)operator(,) ident(certchain_filename)operator(\);) +operator(}) + + +comment(/***************** +evma_get_peername +*****************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_get_peername) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) reserved(struct) ident(sockaddr) operator(*)ident(sa)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_get_peername)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) operator({) + reserved(return) ident(ed)operator(->)ident(GetPeername) operator(()ident(sa)operator(\)) operator(?) integer(1) operator(:) integer(0)operator(;) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) +operator(}) + +comment(/***************** +evma_get_sockname +*****************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_get_sockname) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) reserved(struct) ident(sockaddr) operator(*)ident(sa)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_get_sockname)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) operator({) + reserved(return) ident(ed)operator(->)ident(GetSockname) operator(()ident(sa)operator(\)) operator(?) integer(1) operator(:) integer(0)operator(;) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) +operator(}) + +comment(/*********************** +evma_get_subprocess_pid +***********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_get_subprocess_pid) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) ident(pid_t) operator(*)ident(pid)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_get_subprocess_pid)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) operator({) + reserved(return) ident(ed)operator(->)ident(GetSubprocessPid) operator(()ident(pid)operator(\)) operator(?) integer(1) operator(:) integer(0)operator(;) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) +operator(}) + +comment(/************************** +evma_get_subprocess_status +**************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_get_subprocess_status) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) pre_type(int) operator(*)ident(status)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_get_subprocess_status)delimiter(")>operator(\);) + reserved(if) operator(()ident(status)operator(\)) operator({) + operator(*)ident(status) operator(=) ident(EventMachine)operator(->)ident(SubprocessExitStatus)operator(;) + reserved(return) integer(1)operator(;) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) +operator(}) + + +comment(/********************* +evma_signal_loopbreak +*********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_signal_loopbreak)operator((\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_signal_loopbreak)delimiter(")>operator(\);) + ident(EventMachine)operator(->)ident(SignalLoopBreaker)operator((\);) +operator(}) + + + +comment(/**************** +evma__write_file +****************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma__write_file) operator(()directive(const) pre_type(char) operator(*)ident(filename)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma__write_file)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(_OpenFileForWriting) operator(()ident(filename)operator(\);) +operator(}) + + +comment(/******************************** +evma_get_comm_inactivity_timeout +********************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_get_comm_inactivity_timeout) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) pre_type(int) operator(*)ident(value)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_get_comm_inactivity_timeout)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) operator({) + reserved(return) ident(ed)operator(->)ident(GetCommInactivityTimeout) operator(()ident(value)operator(\);) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) comment(//Perhaps this should be an exception. Access to an unknown binding.) +operator(}) + +comment(/******************************** +evma_set_comm_inactivity_timeout +********************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_set_comm_inactivity_timeout) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) pre_type(int) operator(*)ident(value)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_set_comm_inactivity_timeout)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) operator({) + reserved(return) ident(ed)operator(->)ident(SetCommInactivityTimeout) operator(()ident(value)operator(\);) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) comment(//Perhaps this should be an exception. Access to an unknown binding.) +operator(}) + + +comment(/********************** +evma_set_timer_quantum +**********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_set_timer_quantum) operator(()pre_type(int) ident(interval)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_set_timer_quantum)delimiter(")>operator(\);) + ident(EventMachine)operator(->)ident(SetTimerQuantum) operator(()ident(interval)operator(\);) +operator(}) + +comment(/************************ +evma_set_max_timer_count +************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_set_max_timer_count) operator(()pre_type(int) ident(ct)operator(\)) +operator({) + comment(// This may only be called if the reactor is not running.) + + reserved(if) operator(()ident(EventMachine)operator(\)) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(rb_raise)operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(eventmachine already initialized: evma_set_max_timer_count)delimiter(")>operator(\);) + preprocessor(#else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(eventmachine already initialized: evma_set_max_timer_count)delimiter(")>operator(\);) + preprocessor(#endif) + ident(EventMachine_t)operator(::)ident(SetMaxTimerCount) operator(()ident(ct)operator(\);) +operator(}) + +comment(/****************** +evma_setuid_string +******************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma_setuid_string) operator(()directive(const) pre_type(char) operator(*)ident(username)operator(\)) +operator({) + comment(// We do NOT need to be running an EM instance because this method is static.) + ident(EventMachine_t)operator(::)ident(SetuidString) operator(()ident(username)operator(\);) +operator(}) + + +comment(/********** +evma_popen +**********/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(const) pre_type(char) operator(*)ident(evma_popen) operator(()pre_type(char) operator(*) directive(const)operator(*)ident(cmd_strings)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_popen)delimiter(")>operator(\);) + reserved(return) ident(EventMachine)operator(->)ident(Socketpair) operator(()ident(cmd_strings)operator(\);) +operator(}) + + +comment(/*************************** +evma_get_outbound_data_size +***************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_get_outbound_data_size) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_get_outbound_data_size)delimiter(")>operator(\);) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(return) ident(ed) operator(?) ident(ed)operator(->)ident(GetOutboundDataSize)operator((\)) operator(:) integer(0)operator(;) +operator(}) + + +comment(/*********** +evma__epoll +***********/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma__epoll)operator((\)) +operator({) + ident(bUseEpoll) operator(=) integer(1)operator(;) +operator(}) + +comment(/************ +evma__kqueue +************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(evma__kqueue)operator((\)) +operator({) + ident(bUseKqueue) operator(=) integer(1)operator(;) +operator(}) + + +comment(/********************** +evma_set_rlimit_nofile +**********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_set_rlimit_nofile) operator(()pre_type(int) ident(nofiles)operator(\)) +operator({) + reserved(return) ident(EventMachine_t)operator(::)ident(SetRlimitNofile) operator(()ident(nofiles)operator(\);) +operator(}) + + +comment(/********************************* +evma_send_file_data_to_connection +*********************************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(evma_send_file_data_to_connection) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) directive(const) pre_type(char) operator(*)ident(filename)operator(\)) +operator({) + comment(/* This is a sugaring over send_data_to_connection that reads a file into a + * locally-allocated buffer, and sends the file data to the remote peer. + * Return the number of bytes written to the caller. + * TODO, needs to impose a limit on the file size. This is intended only for + * small files. (I don't know, maybe 8K or less.\) For larger files, use interleaved + * I/O to avoid slowing the rest of the system down. + * TODO: we should return a code rather than barf, in case of file-not-found. + * TODO, does this compile on Windows? + * TODO, given that we want this to work only with small files, how about allocating + * the buffer on the stack rather than the heap? + * + * Modified 25Jul07. This now returns -1 on file-too-large; 0 for success, and a positive + * errno in case of other errors. + * + /* Contributed by Kirk Haines. + */) + + pre_type(char) ident(data)operator([)integer(32)operator(*)integer(1024)operator(];) + pre_type(int) ident(r)operator(;) + + ident(ensure_eventmachine)operator(()string<delimiter(")content(evma_send_file_data_to_connection)delimiter(")>operator(\);) + + pre_type(int) ident(Fd) operator(=) ident(open) operator(()ident(filename)operator(,) ident(O_RDONLY)operator(\);) + + reserved(if) operator(()ident(Fd) operator(<) integer(0)operator(\)) + reserved(return) ident(errno)operator(;) + comment(// From here on, all early returns MUST close Fd.) + + reserved(struct) ident(stat) ident(st)operator(;) + reserved(if) operator(()ident(fstat) operator(()ident(Fd)operator(,) operator(&)ident(st)operator(\)\)) operator({) + pre_type(int) ident(e) operator(=) ident(errno)operator(;) + ident(close) operator(()ident(Fd)operator(\);) + reserved(return) ident(e)operator(;) + operator(}) + + pre_type(int) ident(filesize) operator(=) ident(st)operator(.)ident(st_size)operator(;) + reserved(if) operator(()ident(filesize) operator(<=) integer(0)operator(\)) operator({) + ident(close) operator(()ident(Fd)operator(\);) + reserved(return) integer(0)operator(;) + operator(}) + reserved(else) reserved(if) operator(()ident(filesize) operator(>) reserved(sizeof)operator(()ident(data)operator(\)\)) operator({) + ident(close) operator(()ident(Fd)operator(\);) + reserved(return) operator(-)integer(1)operator(;) + operator(}) + + + ident(r) operator(=) ident(read) operator(()ident(Fd)operator(,) ident(data)operator(,) ident(filesize)operator(\);) + reserved(if) operator(()ident(r) operator(!=) ident(filesize)operator(\)) operator({) + pre_type(int) ident(e) operator(=) ident(errno)operator(;) + ident(close) operator(()ident(Fd)operator(\);) + reserved(return) ident(e)operator(;) + operator(}) + ident(evma_send_data_to_connection) operator(()ident(binding)operator(,) ident(data)operator(,) ident(r)operator(\);) + ident(close) operator(()ident(Fd)operator(\);) + + reserved(return) integer(0)operator(;) +operator(}) + +comment(/***************************************************************************** + +$Id$ + +File: cplusplus.cpp +Date: 27Jul07 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + +preprocessor(#include) include("project.h") + + +reserved(namespace) ident(EM) operator({) + directive(static) ident(map)operator(<)pre_type(string)operator(,) ident(Eventable)operator(*>) ident(Eventables)operator(;) + directive(static) ident(map)operator(<)pre_type(string)operator(,) directive(void)operator((*\)(\)>) ident(Timers)operator(;) +operator(}) + + +comment(/******* +EM::Run +*******/) + +directive(void) ident(EM)operator(::)ident(Run) operator(()directive(void) operator((*)ident(start_func)operator(\)(\)\)) +operator({) + ident(evma__epoll)operator((\);) + ident(evma_initialize_library) operator(()ident(EM)operator(::)ident(Callback)operator(\);) + reserved(if) operator(()ident(start_func)operator(\)) + ident(AddTimer) operator(()integer(0)operator(,) ident(start_func)operator(\);) + ident(evma_run_machine)operator((\);) + ident(evma_release_library)operator((\);) +operator(}) + +comment(/************ +EM::AddTimer +************/) + +directive(void) ident(EM)operator(::)ident(AddTimer) operator(()pre_type(int) ident(milliseconds)operator(,) directive(void) operator((*)ident(func)operator(\)(\)\)) +operator({) + reserved(if) operator(()ident(func)operator(\)) operator({) + directive(const) pre_type(char) operator(*)ident(sig) operator(=) ident(evma_install_oneshot_timer) operator(()ident(milliseconds)operator(\);) + ident(Timers)operator(.)ident(insert) operator(()ident(make_pair) operator(()ident(sig)operator(,) ident(func)operator(\)\);) + operator(}) +operator(}) + + +comment(/*************** +EM::StopReactor +***************/) + +directive(void) ident(EM)operator(::)ident(StopReactor)operator((\)) +operator({) + ident(evma_stop_machine)operator((\);) +operator(}) + + +comment(/******************** +EM::Acceptor::Accept +********************/) + +directive(void) ident(EM)operator(::)ident(Acceptor)operator(::)ident(Accept) operator(()directive(const) pre_type(char) operator(*)ident(signature)operator(\)) +operator({) + ident(Connection) operator(*)ident(c) operator(=) ident(MakeConnection)operator((\);) + ident(c)operator(->)ident(Signature) operator(=) ident(signature)operator(;) + ident(Eventables)operator(.)ident(insert) operator(()ident(make_pair) operator(()ident(c)operator(->)ident(Signature)operator(,) ident(c)operator(\)\);) + ident(c)operator(->)ident(PostInit)operator((\);) +operator(}) + +comment(/************************ +EM::Connection::SendData +************************/) + +directive(void) ident(EM)operator(::)ident(Connection)operator(::)ident(SendData) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(\)) +operator({) + reserved(if) operator(()ident(data)operator(\)) + ident(SendData) operator(()ident(data)operator(,) ident(strlen) operator(()ident(data)operator(\)\);) +operator(}) + + +comment(/************************ +EM::Connection::SendData +************************/) + +directive(void) ident(EM)operator(::)ident(Connection)operator(::)ident(SendData) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(\)) +operator({) + ident(evma_send_data_to_connection) operator(()ident(Signature)operator(.)ident(c_str)operator((\),) ident(data)operator(,) ident(length)operator(\);) +operator(}) + + +comment(/********************* +EM::Connection::Close +*********************/) + +directive(void) ident(EM)operator(::)ident(Connection)operator(::)ident(Close) operator(()pre_type(bool) ident(afterWriting)operator(\)) +operator({) + ident(evma_close_connection) operator(()ident(Signature)operator(.)ident(c_str)operator((\),) ident(afterWriting)operator(\);) +operator(}) + + +comment(/*********************** +EM::Connection::Connect +***********************/) + +directive(void) ident(EM)operator(::)ident(Connection)operator(::)ident(Connect) operator(()directive(const) pre_type(char) operator(*)ident(host)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(Signature) operator(=) ident(evma_connect_to_server) operator(()ident(host)operator(,) ident(port)operator(\);) + ident(Eventables)operator(.)ident(insert)operator(() ident(make_pair) operator(()ident(Signature)operator(,) local_variable(this)operator(\)\);) +operator(}) + +comment(/******************* +EM::Acceptor::Start +*******************/) + +directive(void) ident(EM)operator(::)ident(Acceptor)operator(::)ident(Start) operator(()directive(const) pre_type(char) operator(*)ident(host)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(Signature) operator(=) ident(evma_create_tcp_server) operator(()ident(host)operator(,) ident(port)operator(\);) + ident(Eventables)operator(.)ident(insert)operator(() ident(make_pair) operator(()ident(Signature)operator(,) local_variable(this)operator(\)\);) +operator(}) + + + +comment(/************ +EM::Callback +************/) + +directive(void) ident(EM)operator(::)ident(Callback) operator(()directive(const) pre_type(char) operator(*)ident(sig)operator(,) pre_type(int) ident(ev)operator(,) directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(\)) +operator({) + ident(EM)operator(::)ident(Eventable) operator(*)ident(e)operator(;) + directive(void) operator((*)ident(f)operator(\)(\);) + + reserved(switch) operator(()ident(ev)operator(\)) operator({) + reserved(case) label(EM_TIMER_FIRED)operator(:) + ident(f) operator(=) ident(Timers) operator([)ident(data)operator(];) + reserved(if) operator(()ident(f)operator(\)) + operator((*)ident(f)operator(\)(\);) + ident(Timers)operator(.)ident(erase) operator(()ident(sig)operator(\);) + reserved(break)operator(;) + + reserved(case) label(EM_CONNECTION_READ)operator(:) + ident(e) operator(=) ident(EM)operator(::)ident(Eventables) operator([)ident(sig)operator(];) + ident(e)operator(->)ident(ReceiveData) operator(()ident(data)operator(,) ident(length)operator(\);) + reserved(break)operator(;) + + reserved(case) label(EM_CONNECTION_COMPLETED)operator(:) + ident(e) operator(=) ident(EM)operator(::)ident(Eventables) operator([)ident(sig)operator(];) + ident(e)operator(->)ident(ConnectionCompleted)operator((\);) + reserved(break)operator(;) + + reserved(case) label(EM_CONNECTION_ACCEPTED)operator(:) + ident(e) operator(=) ident(EM)operator(::)ident(Eventables) operator([)ident(sig)operator(];) + ident(e)operator(->)ident(Accept) operator(()ident(data)operator(\);) + reserved(break)operator(;) + + reserved(case) label(EM_CONNECTION_UNBOUND)operator(:) + ident(e) operator(=) ident(EM)operator(::)ident(Eventables) operator([)ident(sig)operator(];) + ident(e)operator(->)ident(Unbind)operator((\);) + ident(EM)operator(::)ident(Eventables)operator(.)ident(erase) operator(()ident(sig)operator(\);) + reserved(delete) ident(e)operator(;) + reserved(break)operator(;) + operator(}) +operator(}) + +comment(/***************************************************************************** + +$Id$ + +File: ed.cpp +Date: 06Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + + + +comment(/******************** +SetSocketNonblocking +********************/) + +pre_type(bool) ident(SetSocketNonblocking) operator(()ident(SOCKET) ident(sd)operator(\)) +operator({) + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(val) operator(=) ident(fcntl) operator(()ident(sd)operator(,) ident(F_GETFL)operator(,) integer(0)operator(\);) + reserved(return) operator(()ident(fcntl) operator(()ident(sd)operator(,) ident(F_SETFL)operator(,) ident(val) operator(|) ident(O_NONBLOCK)operator(\)) operator(!=) ident(SOCKET_ERROR)operator(\)) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(;) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + pre_type(unsigned) pre_type(long) ident(one) operator(=) integer(1)operator(;) + reserved(return) operator(()ident(ioctlsocket) operator(()ident(sd)operator(,) ident(FIONBIO)operator(,) operator(&)ident(one)operator(\)) operator(==) integer(0)operator(\)) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/**************************************** +EventableDescriptor::EventableDescriptor +****************************************/) + +ident(EventableDescriptor)operator(::)ident(EventableDescriptor) operator(()pre_type(int) ident(sd)operator(,) ident(EventMachine_t) operator(*)ident(em)operator(\):) + ident(bCloseNow) operator(()pre_constant(false)operator(\),) + ident(bCloseAfterWriting) operator(()pre_constant(false)operator(\),) + ident(MySocket) operator(()ident(sd)operator(\),) + ident(EventCallback) operator(()pre_constant(NULL)operator(\),) + ident(LastRead) operator(()integer(0)operator(\),) + ident(LastWritten) operator(()integer(0)operator(\),) + ident(bCallbackUnbind) operator(()pre_constant(true)operator(\),) + ident(MyEventMachine) operator(()ident(em)operator(\)) +operator({) + comment(/* There are three ways to close a socket, all of which should + * automatically signal to the event machine that this object + * should be removed from the polling scheduler. + * First is a hard close, intended for bad errors or possible + * security violations. It immediately closes the connection + * and puts this object into an error state. + * Second is to set bCloseNow, which will cause the event machine + * to delete this object (and thus close the connection in our + * destructor\) the next chance it gets. bCloseNow also inhibits + * the writing of new data on the socket (but not necessarily + * the reading of new data\). + * The third way is to set bCloseAfterWriting, which inhibits + * the writing of new data and converts to bCloseNow as soon + * as everything in the outbound queue has been written. + * bCloseAfterWriting is really for use only by protocol handlers + * (for example, HTTP writes an HTML page and then closes the + * connection\). All of the error states we generate internally + * cause an immediate close to be scheduled, which may have the + * effect of discarding outbound data. + */) + + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad eventable descriptor)delimiter(")>operator(\);) + reserved(if) operator(()ident(MyEventMachine) operator(==) pre_constant(NULL)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad em in eventable descriptor)delimiter(")>operator(\);) + ident(CreatedAt) operator(=) ident(gCurrentLoopTime)operator(;) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(data)operator(.)ident(ptr) operator(=) local_variable(this)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/***************************************** +EventableDescriptor::~EventableDescriptor +*****************************************/) + +ident(EventableDescriptor)operator(::~)ident(EventableDescriptor)operator((\)) +operator({) + reserved(if) operator(()ident(EventCallback) operator(&&) ident(bCallbackUnbind)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_UNBOUND)operator(,) pre_constant(NULL)operator(,) integer(0)operator(\);) + ident(Close)operator((\);) +operator(}) + + +comment(/************************************* +EventableDescriptor::SetEventCallback +*************************************/) + +directive(void) ident(EventableDescriptor)operator(::)ident(SetEventCallback) operator(()directive(void)operator((*)ident(cb)operator(\)()directive(const) pre_type(char)operator(*,) pre_type(int)operator(,) directive(const) pre_type(char)operator(*,) pre_type(int)operator(\)\)) +operator({) + ident(EventCallback) operator(=) ident(cb)operator(;) +operator(}) + + +comment(/************************** +EventableDescriptor::Close +**************************/) + +directive(void) ident(EventableDescriptor)operator(::)ident(Close)operator((\)) +operator({) + comment(// Close the socket right now. Intended for emergencies.) + reserved(if) operator(()ident(MySocket) operator(!=) ident(INVALID_SOCKET)operator(\)) operator({) + ident(shutdown) operator(()ident(MySocket)operator(,) integer(1)operator(\);) + ident(closesocket) operator(()ident(MySocket)operator(\);) + ident(MySocket) operator(=) ident(INVALID_SOCKET)operator(;) + operator(}) +operator(}) + + +comment(/********************************* +EventableDescriptor::ShouldDelete +*********************************/) + +pre_type(bool) ident(EventableDescriptor)operator(::)ident(ShouldDelete)operator((\)) +operator({) + comment(/* For use by a socket manager, which needs to know if this object + * should be removed from scheduling events and deleted. + * Has an immediate close been scheduled, or are we already closed? + * If either of these are the case, return true. In theory, the manager will + * then delete us, which in turn will make sure the socket is closed. + * Note, if bCloseAfterWriting is true, we check a virtual method to see + * if there is outbound data to write, and only request a close if there is none. + */) + + reserved(return) operator((()ident(MySocket) operator(==) ident(INVALID_SOCKET)operator(\)) operator(||) ident(bCloseNow) operator(||) operator(()ident(bCloseAfterWriting) operator(&&) operator(()ident(GetOutboundDataSize)operator((\)) operator(<=) integer(0)operator(\)\)\);) +operator(}) + + +comment(/********************************** +EventableDescriptor::ScheduleClose +**********************************/) + +directive(void) ident(EventableDescriptor)operator(::)ident(ScheduleClose) operator(()pre_type(bool) ident(after_writing)operator(\)) +operator({) + comment(// KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled.) + reserved(if) operator(()ident(after_writing)operator(\)) + ident(bCloseAfterWriting) operator(=) pre_constant(true)operator(;) + reserved(else) + ident(bCloseNow) operator(=) pre_constant(true)operator(;) +operator(}) + + +comment(/************************************* +EventableDescriptor::IsCloseScheduled +*************************************/) + +pre_type(bool) ident(EventableDescriptor)operator(::)ident(IsCloseScheduled)operator((\)) +operator({) + comment(// KEEP THIS SYNCHRONIZED WITH ::ScheduleClose.) + reserved(return) operator(()ident(bCloseNow) operator(||) ident(bCloseAfterWriting)operator(\);) +operator(}) + + +comment(/****************************************** +ConnectionDescriptor::ConnectionDescriptor +******************************************/) + +ident(ConnectionDescriptor)operator(::)ident(ConnectionDescriptor) operator(()pre_type(int) ident(sd)operator(,) ident(EventMachine_t) operator(*)ident(em)operator(\):) + ident(EventableDescriptor) operator(()ident(sd)operator(,) ident(em)operator(\),) + ident(bConnectPending) operator(()pre_constant(false)operator(\),) + ident(bNotifyReadable) operator(()pre_constant(false)operator(\),) + ident(bNotifyWritable) operator(()pre_constant(false)operator(\),) + ident(bReadAttemptedAfterClose) operator(()pre_constant(false)operator(\),) + ident(bWriteAttemptedAfterClose) operator(()pre_constant(false)operator(\),) + ident(OutboundDataSize) operator(()integer(0)operator(\),) + preprocessor(#ifdef) ident(WITH_SSL) + ident(SslBox) operator(()pre_constant(NULL)operator(\),) + preprocessor(#endif) + ident(bIsServer) operator(()pre_constant(false)operator(\),) + ident(LastIo) operator(()ident(gCurrentLoopTime)operator(\),) + ident(InactivityTimeout) operator(()integer(0)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLOUT)operator(;) + preprocessor(#endif) + comment(// 22Jan09: Moved ArmKqueueWriter into SetConnectPending(\) to fix assertion failure in _WriteOutboundData(\)) +operator(}) + + +comment(/******************************************* +ConnectionDescriptor::~ConnectionDescriptor +*******************************************/) + +ident(ConnectionDescriptor)operator(::~)ident(ConnectionDescriptor)operator((\)) +operator({) + comment(// Run down any stranded outbound data.) + reserved(for) operator(()ident(size_t) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(OutboundPages)operator(.)ident(size)operator((\);) ident(i)operator(++\)) + ident(OutboundPages)operator([)ident(i)operator(])operator(.)ident(Free)operator((\);) + + preprocessor(#ifdef) ident(WITH_SSL) + reserved(if) operator(()ident(SslBox)operator(\)) + reserved(delete) ident(SslBox)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/************************************************** +STATIC: ConnectionDescriptor::SendDataToConnection +**************************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(SendDataToConnection) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(data_length)operator(\)) +operator({) + comment(// TODO: This is something of a hack, or at least it's a static method of the wrong class.) + comment(// TODO: Poor polymorphism here. We should be calling one virtual method) + comment(// instead of hacking out the runtime information of the target object.) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(dynamic_cast) operator(<)ident(ConnectionDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(cd)operator(\)) + reserved(return) ident(cd)operator(->)ident(SendOutboundData) operator(()ident(data)operator(,) ident(data_length)operator(\);) + ident(DatagramDescriptor) operator(*)ident(ds) operator(=) reserved(dynamic_cast) operator(<)ident(DatagramDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ds)operator(\)) + reserved(return) ident(ds)operator(->)ident(SendOutboundData) operator(()ident(data)operator(,) ident(data_length)operator(\);) + preprocessor(#ifdef) ident(OS_UNIX) + ident(PipeDescriptor) operator(*)ident(ps) operator(=) reserved(dynamic_cast) operator(<)ident(PipeDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ps)operator(\)) + reserved(return) ident(ps)operator(->)ident(SendOutboundData) operator(()ident(data)operator(,) ident(data_length)operator(\);) + preprocessor(#endif) + reserved(return) operator(-)integer(1)operator(;) +operator(}) + + +comment(/********************************************* +STATIC: ConnectionDescriptor::CloseConnection +*********************************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(CloseConnection) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) pre_type(bool) ident(after_writing)operator(\)) +operator({) + comment(// TODO: This is something of a hack, or at least it's a static method of the wrong class.) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) reserved(dynamic_cast) operator(<)ident(EventableDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ed)operator(\)) + ident(ed)operator(->)ident(ScheduleClose) operator(()ident(after_writing)operator(\);) +operator(}) + +comment(/*********************************************** +STATIC: ConnectionDescriptor::ReportErrorStatus +***********************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(ReportErrorStatus) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + comment(// TODO: This is something of a hack, or at least it's a static method of the wrong class.) + comment(// TODO: Poor polymorphism here. We should be calling one virtual method) + comment(// instead of hacking out the runtime information of the target object.) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(dynamic_cast) operator(<)ident(ConnectionDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(cd)operator(\)) + reserved(return) ident(cd)operator(->)ident(_ReportErrorStatus)operator((\);) + reserved(return) operator(-)integer(1)operator(;) +operator(}) + +comment(/*************************************** +ConnectionDescriptor::SetConnectPending +****************************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(SetConnectPending)operator(()pre_type(bool) ident(f)operator(\)) +operator({) + ident(bConnectPending) operator(=) ident(f)operator(;) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueWriter) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/************************************** +ConnectionDescriptor::SendOutboundData +**************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(SendOutboundData) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(\)) +operator({) + preprocessor(#ifdef) ident(WITH_SSL) + reserved(if) operator(()ident(SslBox)operator(\)) operator({) + reserved(if) operator(()ident(length) operator(>) integer(0)operator(\)) operator({) + pre_type(int) ident(w) operator(=) ident(SslBox)operator(->)ident(PutPlaintext) operator(()ident(data)operator(,) ident(length)operator(\);) + reserved(if) operator(()ident(w) operator(<) integer(0)operator(\)) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + reserved(else) + ident(_DispatchCiphertext)operator((\);) + operator(}) + comment(// TODO: What's the correct return value?) + reserved(return) integer(1)operator(;) comment(// That's a wild guess, almost certainly wrong.) + operator(}) + reserved(else) + preprocessor(#endif) + reserved(return) ident(_SendRawOutboundData) operator(()ident(data)operator(,) ident(length)operator(\);) +operator(}) + + + +comment(/****************************************** +ConnectionDescriptor::_SendRawOutboundData +******************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(_SendRawOutboundData) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(\)) +operator({) + comment(/* This internal method is called to schedule bytes that + * will be sent out to the remote peer. + * It's not directly accessed by the caller, who hits ::SendOutboundData, + * which may or may not filter or encrypt the caller's data before + * sending it here. + */) + + comment(// Highly naive and incomplete implementation.) + comment(// There's no throttle for runaways (which should abort only this connection) + comment(// and not the whole process\), and no coalescing of small pages.) + comment(// (Well, not so bad, small pages are coalesced in ::Write\)) + + reserved(if) operator(()ident(IsCloseScheduled)operator((\)\)) + comment(//if (bCloseNow || bCloseAfterWriting\)) + reserved(return) integer(0)operator(;) + + reserved(if) operator((!)ident(data) operator(&&) operator(()ident(length) operator(>) integer(0)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad outbound data)delimiter(")>operator(\);) + pre_type(char) operator(*)ident(buffer) operator(=) operator(()pre_type(char) operator(*\)) ident(malloc) operator(()ident(length) operator(+) integer(1)operator(\);) + reserved(if) operator((!)ident(buffer)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no allocation for outbound data)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(buffer)operator(,) ident(data)operator(,) ident(length)operator(\);) + ident(buffer) operator([)ident(length)operator(]) operator(=) integer(0)operator(;) + ident(OutboundPages)operator(.)ident(push_back) operator(()ident(OutboundPage) operator(()ident(buffer)operator(,) ident(length)operator(\)\);) + ident(OutboundDataSize) operator(+=) ident(length)operator(;) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) ident(EPOLLOUT)operator(\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueWriter) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + reserved(return) ident(length)operator(;) +operator(}) + + + +comment(/*********************************** +ConnectionDescriptor::SelectForRead +***********************************/) + +pre_type(bool) ident(ConnectionDescriptor)operator(::)ident(SelectForRead)operator((\)) +operator({) + comment(/* A connection descriptor is always scheduled for read, + * UNLESS it's in a pending-connect state. + * On Linux, unlike Unix, a nonblocking socket on which + * connect has been called, does NOT necessarily select + * both readable and writable in case of error. + * The socket will select writable when the disposition + * of the connect is known. On the other hand, a socket + * which successfully connects and selects writable may + * indeed have some data available on it, so it will + * select readable in that case, violating expectations! + * So we will not poll for readability until the socket + * is known to be in a connected state. + */) + + reserved(return) ident(bConnectPending) operator(?) pre_constant(false) operator(:) pre_constant(true)operator(;) +operator(}) + + +comment(/************************************ +ConnectionDescriptor::SelectForWrite +************************************/) + +pre_type(bool) ident(ConnectionDescriptor)operator(::)ident(SelectForWrite)operator((\)) +operator({) + comment(/* Cf the notes under SelectForRead. + * In a pending-connect state, we ALWAYS select for writable. + * In a normal state, we only select for writable when we + * have outgoing data to send. + */) + + reserved(if) operator(()ident(bConnectPending) operator(||) ident(bNotifyWritable)operator(\)) + reserved(return) pre_constant(true)operator(;) + reserved(else) operator({) + reserved(return) operator(()ident(GetOutboundDataSize)operator((\)) operator(>) integer(0)operator(\);) + operator(}) +operator(}) + + +comment(/************************** +ConnectionDescriptor::Read +**************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(Read)operator((\)) +operator({) + comment(/* Read and dispatch data on a socket that has selected readable. + * It's theoretically possible to get and dispatch incoming data on + * a socket that has already been scheduled for closing or close-after-writing. + * In those cases, we'll leave it up the to protocol handler to "do the + * right thing" (which probably means to ignore the incoming data\). + * + * 22Aug06: Chris Ochs reports that on FreeBSD, it's possible to come + * here with the socket already closed, after the process receives + * a ctrl-C signal (not sure if that's TERM or INT on BSD\). The application + * was one in which network connections were doing a lot of interleaved reads + * and writes. + * Since we always write before reading (in order to keep the outbound queues + * as light as possible\), I think what happened is that an interrupt caused + * the socket to be closed in ConnectionDescriptor::Write. We'll then + * come here in the same pass through the main event loop, and won't get + * cleaned up until immediately after. + * We originally asserted that the socket was valid when we got here. + * To deal properly with the possibility that we are closed when we get here, + * I removed the assert. HOWEVER, the potential for an infinite loop scares me, + * so even though this is really clunky, I added a flag to assert that we never + * come here more than once after being closed. (FCianfrocca\) + */) + + pre_type(int) ident(sd) operator(=) ident(GetSocket)operator((\);) + comment(//assert (sd != INVALID_SOCKET\); (original, removed 22Aug06\)) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + ident(assert) operator((!)ident(bReadAttemptedAfterClose)operator(\);) + ident(bReadAttemptedAfterClose) operator(=) pre_constant(true)operator(;) + reserved(return)operator(;) + operator(}) + + reserved(if) operator(()ident(bNotifyReadable)operator(\)) operator({) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_NOTIFY_READABLE)operator(,) pre_constant(NULL)operator(,) integer(0)operator(\);) + reserved(return)operator(;) + operator(}) + + ident(LastIo) operator(=) ident(gCurrentLoopTime)operator(;) + + pre_type(int) ident(total_bytes_read) operator(=) integer(0)operator(;) + pre_type(char) ident(readbuffer) operator([)integer(16) operator(*) integer(1024) operator(+) integer(1)operator(];) + + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) integer(10)operator(;) ident(i)operator(++\)) operator({) + comment(// Don't read just one buffer and then move on. This is faster) + comment(// if there is a lot of incoming.) + comment(// But don't read indefinitely. Give other sockets a chance to run.) + comment(// NOTICE, we're reading one less than the buffer size.) + comment(// That's so we can put a guard byte at the end of what we send) + comment(// to user code.) + + + pre_type(int) ident(r) operator(=) ident(recv) operator(()ident(sd)operator(,) ident(readbuffer)operator(,) reserved(sizeof)operator(()ident(readbuffer)operator(\)) operator(-) integer(1)operator(,) integer(0)operator(\);) + comment(//cerr << "<R:" << r << ">";) + + reserved(if) operator(()ident(r) operator(>) integer(0)operator(\)) operator({) + ident(total_bytes_read) operator(+=) ident(r)operator(;) + ident(LastRead) operator(=) ident(gCurrentLoopTime)operator(;) + + comment(// Add a null-terminator at the the end of the buffer) + comment(// that we will send to the callback.) + comment(// DO NOT EVER CHANGE THIS. We want to explicitly allow users) + comment(// to be able to depend on this behavior, so they will have) + comment(// the option to do some things faster. Additionally it's) + comment(// a security guard against buffer overflows.) + ident(readbuffer) operator([)ident(r)operator(]) operator(=) integer(0)operator(;) + ident(_DispatchInboundData) operator(()ident(readbuffer)operator(,) ident(r)operator(\);) + operator(}) + reserved(else) reserved(if) operator(()ident(r) operator(==) integer(0)operator(\)) operator({) + reserved(break)operator(;) + operator(}) + reserved(else) operator({) + comment(// Basically a would-block, meaning we've read everything there is to read.) + reserved(break)operator(;) + operator(}) + + operator(}) + + + reserved(if) operator(()ident(total_bytes_read) operator(==) integer(0)operator(\)) operator({) + comment(// If we read no data on a socket that selected readable,) + comment(// it generally means the other end closed the connection gracefully.) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) + operator(}) + +operator(}) + + + +comment(/****************************************** +ConnectionDescriptor::_DispatchInboundData +******************************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(_DispatchInboundData) operator(()directive(const) pre_type(char) operator(*)ident(buffer)operator(,) pre_type(int) ident(size)operator(\)) +operator({) + preprocessor(#ifdef) ident(WITH_SSL) + reserved(if) operator(()ident(SslBox)operator(\)) operator({) + ident(SslBox)operator(->)ident(PutCiphertext) operator(()ident(buffer)operator(,) ident(size)operator(\);) + + pre_type(int) ident(s)operator(;) + pre_type(char) ident(B) operator([)integer(2048)operator(];) + reserved(while) operator((()ident(s) operator(=) ident(SslBox)operator(->)ident(GetPlaintext) operator(()ident(B)operator(,) reserved(sizeof)operator(()ident(B)operator(\)) operator(-) integer(1)operator(\)\)) operator(>) integer(0)operator(\)) operator({) + ident(B) operator([)ident(s)operator(]) operator(=) integer(0)operator(;) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_READ)operator(,) ident(B)operator(,) ident(s)operator(\);) + operator(}) + comment(// INCOMPLETE, s may indicate an SSL error that would force the connection down.) + ident(_DispatchCiphertext)operator((\);) + operator(}) + reserved(else) operator({) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_READ)operator(,) ident(buffer)operator(,) ident(size)operator(\);) + operator(}) + preprocessor(#endif) + + preprocessor(#ifdef) ident(WITHOUT_SSL) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_READ)operator(,) ident(buffer)operator(,) ident(size)operator(\);) + preprocessor(#endif) +operator(}) + + + + + +comment(/*************************** +ConnectionDescriptor::Write +***************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(Write)operator((\)) +operator({) + comment(/* A socket which is in a pending-connect state will select + * writable when the disposition of the connect is known. + * At that point, check to be sure there are no errors, + * and if none, then promote the socket out of the pending + * state. + * TODO: I haven't figured out how Windows signals errors on + * unconnected sockets. Maybe it does the untraditional but + * logical thing and makes the socket selectable for error. + * If so, it's unsupported here for the time being, and connect + * errors will have to be caught by the timeout mechanism. + */) + + reserved(if) operator(()ident(bConnectPending)operator(\)) operator({) + pre_type(int) ident(error)operator(;) + ident(socklen_t) ident(len)operator(;) + ident(len) operator(=) reserved(sizeof)operator(()ident(error)operator(\);) + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(o) operator(=) ident(getsockopt) operator(()ident(GetSocket)operator((\),) ident(SOL_SOCKET)operator(,) ident(SO_ERROR)operator(,) operator(&)ident(error)operator(,) operator(&)ident(len)operator(\);) + preprocessor(#endif) + preprocessor(#ifdef) ident(OS_WIN32) + pre_type(int) ident(o) operator(=) ident(getsockopt) operator(()ident(GetSocket)operator((\),) ident(SOL_SOCKET)operator(,) ident(SO_ERROR)operator(,) operator(()pre_type(char)operator(*\)&)ident(error)operator(,) operator(&)ident(len)operator(\);) + preprocessor(#endif) + reserved(if) operator((()ident(o) operator(==) integer(0)operator(\)) operator(&&) operator(()ident(error) operator(==) integer(0)operator(\)\)) operator({) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_COMPLETED)operator(,) string<delimiter(")delimiter(")>operator(,) integer(0)operator(\);) + ident(bConnectPending) operator(=) pre_constant(false)operator(;) + preprocessor(#ifdef) ident(HAVE_EPOLL) + comment(// The callback may have scheduled outbound data.) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLIN) operator(|) operator(()ident(SelectForWrite)operator((\)) operator(?) ident(EPOLLOUT) operator(:) integer(0)operator(\);) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()local_variable(this)operator(\);) + comment(// The callback may have scheduled outbound data.) + reserved(if) operator(()ident(SelectForWrite)operator((\)\)) + ident(MyEventMachine)operator(->)ident(ArmKqueueWriter) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + operator(}) + reserved(else) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) + operator(}) + reserved(else) operator({) + + reserved(if) operator(()ident(bNotifyWritable)operator(\)) operator({) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_NOTIFY_WRITABLE)operator(,) pre_constant(NULL)operator(,) integer(0)operator(\);) + reserved(return)operator(;) + operator(}) + + ident(_WriteOutboundData)operator((\);) + operator(}) +operator(}) + + +comment(/**************************************** +ConnectionDescriptor::_WriteOutboundData +****************************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(_WriteOutboundData)operator((\)) +operator({) + comment(/* This is a helper function called by ::Write. + * It's possible for a socket to select writable and then no longer + * be writable by the time we get around to writing. The kernel might + * have used up its available output buffers between the select call + * and when we get here. So this condition is not an error. + * + * 20Jul07, added the same kind of protection against an invalid socket + * that is at the top of ::Read. Not entirely how this could happen in + * real life (connection-reset from the remote peer, perhaps?\), but I'm + * doing it to address some reports of crashing under heavy loads. + */) + + pre_type(int) ident(sd) operator(=) ident(GetSocket)operator((\);) + comment(//assert (sd != INVALID_SOCKET\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + ident(assert) operator((!)ident(bWriteAttemptedAfterClose)operator(\);) + ident(bWriteAttemptedAfterClose) operator(=) pre_constant(true)operator(;) + reserved(return)operator(;) + operator(}) + + ident(LastIo) operator(=) ident(gCurrentLoopTime)operator(;) + pre_type(char) ident(output_buffer) operator([)integer(16) operator(*) integer(1024)operator(];) + ident(size_t) ident(nbytes) operator(=) integer(0)operator(;) + + reserved(while) operator((()ident(OutboundPages)operator(.)ident(size)operator((\)) operator(>) integer(0)operator(\)) operator(&&) operator(()ident(nbytes) operator(<) reserved(sizeof)operator(()ident(output_buffer)operator(\)\)\)) operator({) + ident(OutboundPage) operator(*)ident(op) operator(=) operator(&()ident(OutboundPages)operator([)integer(0)operator(]\);) + reserved(if) operator((()ident(nbytes) operator(+) ident(op)operator(->)ident(Length) operator(-) ident(op)operator(->)ident(Offset)operator(\)) operator(<) reserved(sizeof) operator(()ident(output_buffer)operator(\)\)) operator({) + ident(memcpy) operator(()ident(output_buffer) operator(+) ident(nbytes)operator(,) ident(op)operator(->)ident(Buffer) operator(+) ident(op)operator(->)ident(Offset)operator(,) ident(op)operator(->)ident(Length) operator(-) ident(op)operator(->)ident(Offset)operator(\);) + ident(nbytes) operator(+=) operator(()ident(op)operator(->)ident(Length) operator(-) ident(op)operator(->)ident(Offset)operator(\);) + ident(op)operator(->)ident(Free)operator((\);) + ident(OutboundPages)operator(.)ident(pop_front)operator((\);) + operator(}) + reserved(else) operator({) + pre_type(int) ident(len) operator(=) reserved(sizeof)operator(()ident(output_buffer)operator(\)) operator(-) ident(nbytes)operator(;) + ident(memcpy) operator(()ident(output_buffer) operator(+) ident(nbytes)operator(,) ident(op)operator(->)ident(Buffer) operator(+) ident(op)operator(->)ident(Offset)operator(,) ident(len)operator(\);) + ident(op)operator(->)ident(Offset) operator(+=) ident(len)operator(;) + ident(nbytes) operator(+=) ident(len)operator(;) + operator(}) + operator(}) + + comment(// We should never have gotten here if there were no data to write,) + comment(// so assert that as a sanity check.) + comment(// Don't bother to make sure nbytes is less than output_buffer because) + comment(// if it were we probably would have crashed already.) + ident(assert) operator(()ident(nbytes) operator(>) integer(0)operator(\);) + + ident(assert) operator(()ident(GetSocket)operator((\)) operator(!=) ident(INVALID_SOCKET)operator(\);) + pre_type(int) ident(bytes_written) operator(=) ident(send) operator(()ident(GetSocket)operator((\),) ident(output_buffer)operator(,) ident(nbytes)operator(,) integer(0)operator(\);) + + pre_type(bool) ident(err) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(bytes_written) operator(<) integer(0)operator(\)) operator({) + ident(err) operator(=) pre_constant(true)operator(;) + ident(bytes_written) operator(=) integer(0)operator(;) + operator(}) + + ident(assert) operator(()ident(bytes_written) operator(>=) integer(0)operator(\);) + ident(OutboundDataSize) operator(-=) ident(bytes_written)operator(;) + reserved(if) operator((()ident(size_t)operator(\))ident(bytes_written) operator(<) ident(nbytes)operator(\)) operator({) + pre_type(int) ident(len) operator(=) ident(nbytes) operator(-) ident(bytes_written)operator(;) + pre_type(char) operator(*)ident(buffer) operator(=) operator(()pre_type(char)operator(*\)) ident(malloc) operator(()ident(len) operator(+) integer(1)operator(\);) + reserved(if) operator((!)ident(buffer)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad alloc throwing back data)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(buffer)operator(,) ident(output_buffer) operator(+) ident(bytes_written)operator(,) ident(len)operator(\);) + ident(buffer) operator([)ident(len)operator(]) operator(=) integer(0)operator(;) + ident(OutboundPages)operator(.)ident(push_front) operator(()ident(OutboundPage) operator(()ident(buffer)operator(,) ident(len)operator(\)\);) + operator(}) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) operator(()ident(SelectForWrite)operator((\)) operator(?) ident(EPOLLOUT) operator(:) integer(0)operator(\)\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(if) operator(()ident(SelectForWrite)operator((\)\)) + ident(MyEventMachine)operator(->)ident(ArmKqueueWriter) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + + + reserved(if) operator(()ident(err)operator(\)) operator({) + preprocessor(#ifdef) ident(OS_UNIX) + reserved(if) operator((()ident(errno) operator(!=) ident(EINPROGRESS)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(EWOULDBLOCK)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(EINTR)operator(\)\)) + preprocessor(#endif) + preprocessor(#ifdef) ident(OS_WIN32) + reserved(if) operator((()ident(errno) operator(!=) ident(WSAEINPROGRESS)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(WSAEWOULDBLOCK)operator(\)\)) + preprocessor(#endif) + ident(Close)operator((\);) + operator(}) +operator(}) + + +comment(/**************************************** +ConnectionDescriptor::_ReportErrorStatus +****************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(_ReportErrorStatus)operator((\)) +operator({) + pre_type(int) ident(error)operator(;) + ident(socklen_t) ident(len)operator(;) + ident(len) operator(=) reserved(sizeof)operator(()ident(error)operator(\);) + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(o) operator(=) ident(getsockopt) operator(()ident(GetSocket)operator((\),) ident(SOL_SOCKET)operator(,) ident(SO_ERROR)operator(,) operator(&)ident(error)operator(,) operator(&)ident(len)operator(\);) + preprocessor(#endif) + preprocessor(#ifdef) ident(OS_WIN32) + pre_type(int) ident(o) operator(=) ident(getsockopt) operator(()ident(GetSocket)operator((\),) ident(SOL_SOCKET)operator(,) ident(SO_ERROR)operator(,) operator(()pre_type(char)operator(*\)&)ident(error)operator(,) operator(&)ident(len)operator(\);) + preprocessor(#endif) + reserved(if) operator((()ident(o) operator(==) integer(0)operator(\)) operator(&&) operator(()ident(error) operator(==) integer(0)operator(\)\)) + reserved(return) integer(0)operator(;) + reserved(else) + reserved(return) integer(1)operator(;) +operator(}) + + +comment(/****************************** +ConnectionDescriptor::StartTls +******************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(StartTls)operator((\)) +operator({) + preprocessor(#ifdef) ident(WITH_SSL) + reserved(if) operator(()ident(SslBox)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(SSL/TLS already running on connection)delimiter(")>operator(\);) + + ident(SslBox) operator(=) reserved(new) ident(SslBox_t) operator(()ident(bIsServer)operator(,) ident(PrivateKeyFilename)operator(,) ident(CertChainFilename)operator(\);) + ident(_DispatchCiphertext)operator((\);) + preprocessor(#endif) + + preprocessor(#ifdef) ident(WITHOUT_SSL) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(Encryption not available on this event-machine)delimiter(")>operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/********************************* +ConnectionDescriptor::SetTlsParms +*********************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(SetTlsParms) operator(()directive(const) pre_type(char) operator(*)ident(privkey_filename)operator(,) directive(const) pre_type(char) operator(*)ident(certchain_filename)operator(\)) +operator({) + preprocessor(#ifdef) ident(WITH_SSL) + reserved(if) operator(()ident(SslBox)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(call SetTlsParms before calling StartTls)delimiter(")>operator(\);) + reserved(if) operator(()ident(privkey_filename) operator(&&) operator(*)ident(privkey_filename)operator(\)) + ident(PrivateKeyFilename) operator(=) ident(privkey_filename)operator(;) + reserved(if) operator(()ident(certchain_filename) operator(&&) operator(*)ident(certchain_filename)operator(\)) + ident(CertChainFilename) operator(=) ident(certchain_filename)operator(;) + preprocessor(#endif) + + preprocessor(#ifdef) ident(WITHOUT_SSL) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(Encryption not available on this event-machine)delimiter(")>operator(\);) + preprocessor(#endif) +operator(}) + + + +comment(/***************************************** +ConnectionDescriptor::_DispatchCiphertext +*****************************************/) +preprocessor(#ifdef) ident(WITH_SSL) +directive(void) ident(ConnectionDescriptor)operator(::)ident(_DispatchCiphertext)operator((\)) +operator({) + ident(assert) operator(()ident(SslBox)operator(\);) + + + pre_type(char) ident(BigBuf) operator([)integer(2048)operator(];) + pre_type(bool) ident(did_work)operator(;) + + reserved(do) operator({) + ident(did_work) operator(=) pre_constant(false)operator(;) + + comment(// try to drain ciphertext) + reserved(while) operator(()ident(SslBox)operator(->)ident(CanGetCiphertext)operator((\)\)) operator({) + pre_type(int) ident(r) operator(=) ident(SslBox)operator(->)ident(GetCiphertext) operator(()ident(BigBuf)operator(,) reserved(sizeof)operator(()ident(BigBuf)operator(\)\);) + ident(assert) operator(()ident(r) operator(>) integer(0)operator(\);) + ident(_SendRawOutboundData) operator(()ident(BigBuf)operator(,) ident(r)operator(\);) + ident(did_work) operator(=) pre_constant(true)operator(;) + operator(}) + + comment(// Pump the SslBox, in case it has queued outgoing plaintext) + comment(// This will return >0 if data was written,) + comment(// 0 if no data was written, and <0 if there was a fatal error.) + pre_type(bool) ident(pump)operator(;) + reserved(do) operator({) + ident(pump) operator(=) pre_constant(false)operator(;) + pre_type(int) ident(w) operator(=) ident(SslBox)operator(->)ident(PutPlaintext) operator(()pre_constant(NULL)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(w) operator(>) integer(0)operator(\)) operator({) + ident(did_work) operator(=) pre_constant(true)operator(;) + ident(pump) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(else) reserved(if) operator(()ident(w) operator(<) integer(0)operator(\)) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + operator(}) reserved(while) operator(()ident(pump)operator(\);) + + comment(// try to put plaintext. INCOMPLETE, doesn't belong here?) + comment(// In SendOutboundData, we're spooling plaintext directly) + comment(// into SslBox. That may be wrong, we may need to buffer it) + comment(// up here! ) + comment(/* + const char *ptr; + int ptr_length; + while (OutboundPlaintext.GetPage (&ptr, &ptr_length\)\) { + assert (ptr && (ptr_length > 0\)\); + int w = SslMachine.PutPlaintext (ptr, ptr_length\); + if (w > 0\) { + OutboundPlaintext.DiscardBytes (w\); + did_work = true; + } + else + break; + } + */) + + operator(}) reserved(while) operator(()ident(did_work)operator(\);) + +operator(}) +preprocessor(#endif) + + + +comment(/******************************* +ConnectionDescriptor::Heartbeat +*******************************/) + +directive(void) ident(ConnectionDescriptor)operator(::)ident(Heartbeat)operator((\)) +operator({) + comment(/* Only allow a certain amount of time to go by while waiting + * for a pending connect. If it expires, then kill the socket. + * For a connected socket, close it if its inactivity timer + * has expired. + */) + + reserved(if) operator(()ident(bConnectPending)operator(\)) operator({) + reserved(if) operator((()ident(gCurrentLoopTime) operator(-) ident(CreatedAt)operator(\)) operator(>=) ident(PendingConnectTimeout)operator(\)) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) + operator(}) + reserved(else) operator({) + reserved(if) operator(()ident(InactivityTimeout) operator(&&) operator((()ident(gCurrentLoopTime) operator(-) ident(LastIo)operator(\)) operator(>=) ident(InactivityTimeout)operator(\)\)) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) + operator(}) +operator(}) + + +comment(/**************************************** +LoopbreakDescriptor::LoopbreakDescriptor +****************************************/) + +ident(LoopbreakDescriptor)operator(::)ident(LoopbreakDescriptor) operator(()pre_type(int) ident(sd)operator(,) ident(EventMachine_t) operator(*)ident(parent_em)operator(\):) + ident(EventableDescriptor) operator(()ident(sd)operator(,) ident(parent_em)operator(\)) +operator({) + comment(/* This is really bad and ugly. Change someday if possible. + * We have to know about an event-machine (probably the one that owns us\), + * so we can pass newly-created connections to it. + */) + + ident(bCallbackUnbind) operator(=) pre_constant(false)operator(;) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLIN)operator(;) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + + + +comment(/************************* +LoopbreakDescriptor::Read +*************************/) + +directive(void) ident(LoopbreakDescriptor)operator(::)ident(Read)operator((\)) +operator({) + comment(// TODO, refactor, this code is probably in the wrong place.) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(_ReadLoopBreaker)operator((\);) +operator(}) + + +comment(/************************** +LoopbreakDescriptor::Write +**************************/) + +directive(void) ident(LoopbreakDescriptor)operator(::)ident(Write)operator((\)) +operator({) + comment(// Why are we here?) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad code path in loopbreak)delimiter(")>operator(\);) +operator(}) + +comment(/************************************** +AcceptorDescriptor::AcceptorDescriptor +**************************************/) + +ident(AcceptorDescriptor)operator(::)ident(AcceptorDescriptor) operator(()pre_type(int) ident(sd)operator(,) ident(EventMachine_t) operator(*)ident(parent_em)operator(\):) + ident(EventableDescriptor) operator(()ident(sd)operator(,) ident(parent_em)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLIN)operator(;) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/*************************************** +AcceptorDescriptor::~AcceptorDescriptor +***************************************/) + +ident(AcceptorDescriptor)operator(::~)ident(AcceptorDescriptor)operator((\)) +operator({) +operator(}) + +comment(/**************************************** +STATIC: AcceptorDescriptor::StopAcceptor +****************************************/) + +directive(void) ident(AcceptorDescriptor)operator(::)ident(StopAcceptor) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(\)) +operator({) + comment(// TODO: This is something of a hack, or at least it's a static method of the wrong class.) + ident(AcceptorDescriptor) operator(*)ident(ad) operator(=) reserved(dynamic_cast) operator(<)ident(AcceptorDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(ad)operator(\)) + ident(ad)operator(->)ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + reserved(else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(failed to close nonexistent acceptor)delimiter(")>operator(\);) +operator(}) + + +comment(/************************ +AcceptorDescriptor::Read +************************/) + +directive(void) ident(AcceptorDescriptor)operator(::)ident(Read)operator((\)) +operator({) + comment(/* Accept up to a certain number of sockets on the listening connection. + * Don't try to accept all that are present, because this would allow a DoS attack + * in which no data were ever read or written. We should accept more than one, + * if available, to keep the partially accepted sockets from backing up in the kernel. + */) + + comment(/* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it + * for readability. According to Stevens UNP, it's possible for an acceptor to select readable + * and then block when we call accept. For example, the other end resets the connection after + * the socket selects readable and before we call accept. The kernel will remove the dead + * socket from the accept queue. If the accept queue is now empty, accept will block. + */) + + + reserved(struct) ident(sockaddr_in) ident(pin)operator(;) + ident(socklen_t) ident(addrlen) operator(=) reserved(sizeof) operator(()ident(pin)operator(\);) + + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) integer(10)operator(;) ident(i)operator(++\)) operator({) + pre_type(int) ident(sd) operator(=) ident(accept) operator(()ident(GetSocket)operator((\),) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(pin)operator(,) operator(&)ident(addrlen)operator(\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + comment(// This breaks the loop when we've accepted everything on the kernel queue,) + comment(// up to 10 new connections. But what if the *first* accept fails?) + comment(// Does that mean anything serious is happening, beyond the situation) + comment(// described in the note above?) + reserved(break)operator(;) + operator(}) + + comment(// Set the newly-accepted socket non-blocking.) + comment(// On Windows, this may fail because, weirdly, Windows inherits the non-blocking) + comment(// attribute that we applied to the acceptor socket into the accepted one.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(sd)operator(\)\)) operator({) + comment(//int val = fcntl (sd, F_GETFL, 0\);) + comment(//if (fcntl (sd, F_SETFL, val | O_NONBLOCK\) == -1\) {) + ident(shutdown) operator(()ident(sd)operator(,) integer(1)operator(\);) + ident(closesocket) operator(()ident(sd)operator(\);) + reserved(continue)operator(;) + operator(}) + + + comment(// Disable slow-start (Nagle algorithm\). Eventually make this configurable.) + pre_type(int) ident(one) operator(=) integer(1)operator(;) + ident(setsockopt) operator(()ident(sd)operator(,) ident(IPPROTO_TCP)operator(,) ident(TCP_NODELAY)operator(,) operator(()pre_type(char)operator(*\)) operator(&)ident(one)operator(,) reserved(sizeof)operator(()ident(one)operator(\)\);) + + + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(sd)operator(,) ident(MyEventMachine)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no newly accepted connection)delimiter(")>operator(\);) + ident(cd)operator(->)ident(SetServerMode)operator((\);) + reserved(if) operator(()ident(EventCallback)operator(\)) operator({) + operator((*)ident(EventCallback)operator(\)) operator(()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_ACCEPTED)operator(,) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(size)operator((\)\);) + operator(}) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(cd)operator(->)ident(GetEpollEvent)operator((\)->)ident(events) operator(=) ident(EPOLLIN) operator(|) operator(()ident(cd)operator(->)ident(SelectForWrite)operator((\)) operator(?) ident(EPOLLOUT) operator(:) integer(0)operator(\);) + preprocessor(#endif) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Add) operator(()ident(cd)operator(\);) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(if) operator(()ident(cd)operator(->)ident(SelectForWrite)operator((\)\)) + ident(MyEventMachine)operator(->)ident(ArmKqueueWriter) operator(()ident(cd)operator(\);) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()ident(cd)operator(\);) + preprocessor(#endif) + operator(}) + +operator(}) + + +comment(/************************* +AcceptorDescriptor::Write +*************************/) + +directive(void) ident(AcceptorDescriptor)operator(::)ident(Write)operator((\)) +operator({) + comment(// Why are we here?) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad code path in acceptor)delimiter(")>operator(\);) +operator(}) + + +comment(/***************************** +AcceptorDescriptor::Heartbeat +*****************************/) + +directive(void) ident(AcceptorDescriptor)operator(::)ident(Heartbeat)operator((\)) +operator({) + comment(// No-op) +operator(}) + + +comment(/******************************* +AcceptorDescriptor::GetSockname +*******************************/) + +pre_type(bool) ident(AcceptorDescriptor)operator(::)ident(GetSockname) operator(()reserved(struct) ident(sockaddr) operator(*)ident(s)operator(\)) +operator({) + pre_type(bool) ident(ok) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(s)operator(\)) operator({) + ident(socklen_t) ident(len) operator(=) reserved(sizeof)operator((*)ident(s)operator(\);) + pre_type(int) ident(gp) operator(=) ident(getsockname) operator(()ident(GetSocket)operator((\),) ident(s)operator(,) operator(&)ident(len)operator(\);) + reserved(if) operator(()ident(gp) operator(==) integer(0)operator(\)) + ident(ok) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(return) ident(ok)operator(;) +operator(}) + + + +comment(/************************************** +DatagramDescriptor::DatagramDescriptor +**************************************/) + +ident(DatagramDescriptor)operator(::)ident(DatagramDescriptor) operator(()pre_type(int) ident(sd)operator(,) ident(EventMachine_t) operator(*)ident(parent_em)operator(\):) + ident(EventableDescriptor) operator(()ident(sd)operator(,) ident(parent_em)operator(\),) + ident(OutboundDataSize) operator(()integer(0)operator(\),) + ident(LastIo) operator(()ident(gCurrentLoopTime)operator(\),) + ident(InactivityTimeout) operator(()integer(0)operator(\)) +operator({) + ident(memset) operator((&)ident(ReturnAddress)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(ReturnAddress)operator(\)\);) + + comment(/* Provisionally added 19Oct07. All datagram sockets support broadcasting. + * Until now, sending to a broadcast address would give EACCES (permission denied\) + * on systems like Linux and BSD that require the SO_BROADCAST socket-option in order + * to accept a packet to a broadcast address. Solaris doesn't require it. I think + * Windows DOES require it but I'm not sure. + * + * Ruby does NOT do what we're doing here. In Ruby, you have to explicitly set SO_BROADCAST + * on a UDP socket in order to enable broadcasting. The reason for requiring the option + * in the first place is so that applications don't send broadcast datagrams by mistake. + * I imagine that could happen if a user of an application typed in an address that happened + * to be a broadcast address on that particular subnet. + * + * This is provisional because someone may eventually come up with a good reason not to + * do it for all UDP sockets. If that happens, then we'll need to add a usercode-level API + * to set the socket option, just like Ruby does. AND WE'LL ALSO BREAK CODE THAT DOESN'T + * EXPLICITLY SET THE OPTION. + */) + + pre_type(int) ident(oval) operator(=) integer(1)operator(;) + pre_type(int) ident(sob) operator(=) ident(setsockopt) operator(()ident(GetSocket)operator((\),) ident(SOL_SOCKET)operator(,) ident(SO_BROADCAST)operator(,) operator(()pre_type(char)operator(*\)&)ident(oval)operator(,) reserved(sizeof)operator(()ident(oval)operator(\)\);) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLIN)operator(;) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/*************************************** +DatagramDescriptor::~DatagramDescriptor +***************************************/) + +ident(DatagramDescriptor)operator(::~)ident(DatagramDescriptor)operator((\)) +operator({) + comment(// Run down any stranded outbound data.) + reserved(for) operator(()ident(size_t) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(OutboundPages)operator(.)ident(size)operator((\);) ident(i)operator(++\)) + ident(OutboundPages)operator([)ident(i)operator(])operator(.)ident(Free)operator((\);) +operator(}) + + +comment(/***************************** +DatagramDescriptor::Heartbeat +*****************************/) + +directive(void) ident(DatagramDescriptor)operator(::)ident(Heartbeat)operator((\)) +operator({) + comment(// Close it if its inactivity timer has expired.) + + reserved(if) operator(()ident(InactivityTimeout) operator(&&) operator((()ident(gCurrentLoopTime) operator(-) ident(LastIo)operator(\)) operator(>=) ident(InactivityTimeout)operator(\)\)) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) +operator(}) + + +comment(/************************ +DatagramDescriptor::Read +************************/) + +directive(void) ident(DatagramDescriptor)operator(::)ident(Read)operator((\)) +operator({) + pre_type(int) ident(sd) operator(=) ident(GetSocket)operator((\);) + ident(assert) operator(()ident(sd) operator(!=) ident(INVALID_SOCKET)operator(\);) + ident(LastIo) operator(=) ident(gCurrentLoopTime)operator(;) + + comment(// This is an extremely large read buffer.) + comment(// In many cases you wouldn't expect to get any more than 4K.) + pre_type(char) ident(readbuffer) operator([)integer(16) operator(*) integer(1024)operator(];) + + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) integer(10)operator(;) ident(i)operator(++\)) operator({) + comment(// Don't read just one buffer and then move on. This is faster) + comment(// if there is a lot of incoming.) + comment(// But don't read indefinitely. Give other sockets a chance to run.) + comment(// NOTICE, we're reading one less than the buffer size.) + comment(// That's so we can put a guard byte at the end of what we send) + comment(// to user code.) + + reserved(struct) ident(sockaddr_in) ident(sin)operator(;) + ident(socklen_t) ident(slen) operator(=) reserved(sizeof) operator(()ident(sin)operator(\);) + ident(memset) operator((&)ident(sin)operator(,) integer(0)operator(,) ident(slen)operator(\);) + + pre_type(int) ident(r) operator(=) ident(recvfrom) operator(()ident(sd)operator(,) ident(readbuffer)operator(,) reserved(sizeof)operator(()ident(readbuffer)operator(\)) operator(-) integer(1)operator(,) integer(0)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(sin)operator(,) operator(&)ident(slen)operator(\);) + comment(//cerr << "<R:" << r << ">";) + + comment(// In UDP, a zero-length packet is perfectly legal.) + reserved(if) operator(()ident(r) operator(>=) integer(0)operator(\)) operator({) + ident(LastRead) operator(=) ident(gCurrentLoopTime)operator(;) + + comment(// Add a null-terminator at the the end of the buffer) + comment(// that we will send to the callback.) + comment(// DO NOT EVER CHANGE THIS. We want to explicitly allow users) + comment(// to be able to depend on this behavior, so they will have) + comment(// the option to do some things faster. Additionally it's) + comment(// a security guard against buffer overflows.) + ident(readbuffer) operator([)ident(r)operator(]) operator(=) integer(0)operator(;) + + + comment(// Set up a "temporary" return address so that callers can "reply" to us) + comment(// from within the callback we are about to invoke. That means that ordinary) + comment(// calls to "send_data_to_connection" (which is of course misnamed in this) + comment(// case\) will result in packets being sent back to the same place that sent) + comment(// us this one.) + comment(// There is a different call (evma_send_datagram\) for cases where the caller) + comment(// actually wants to send a packet somewhere else.) + + ident(memset) operator((&)ident(ReturnAddress)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(ReturnAddress)operator(\)\);) + ident(memcpy) operator((&)ident(ReturnAddress)operator(,) operator(&)ident(sin)operator(,) ident(slen)operator(\);) + + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_READ)operator(,) ident(readbuffer)operator(,) ident(r)operator(\);) + + operator(}) + reserved(else) operator({) + comment(// Basically a would-block, meaning we've read everything there is to read.) + reserved(break)operator(;) + operator(}) + + operator(}) + + +operator(}) + + +comment(/************************* +DatagramDescriptor::Write +*************************/) + +directive(void) ident(DatagramDescriptor)operator(::)ident(Write)operator((\)) +operator({) + comment(/* It's possible for a socket to select writable and then no longer + * be writable by the time we get around to writing. The kernel might + * have used up its available output buffers between the select call + * and when we get here. So this condition is not an error. + * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData, + * but differs in the that the outbound data pages (received from the + * user\) are _message-structured._ That is, we send each of them out + * one message at a time. + * TODO, we are currently suppressing the EMSGSIZE error!!! + */) + + pre_type(int) ident(sd) operator(=) ident(GetSocket)operator((\);) + ident(assert) operator(()ident(sd) operator(!=) ident(INVALID_SOCKET)operator(\);) + ident(LastIo) operator(=) ident(gCurrentLoopTime)operator(;) + + ident(assert) operator(()ident(OutboundPages)operator(.)ident(size)operator((\)) operator(>) integer(0)operator(\);) + + comment(// Send out up to 10 packets, then cycle the machine.) + reserved(for) operator(()pre_type(int) ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) integer(10)operator(;) ident(i)operator(++\)) operator({) + reserved(if) operator(()ident(OutboundPages)operator(.)ident(size)operator((\)) operator(<=) integer(0)operator(\)) + reserved(break)operator(;) + ident(OutboundPage) operator(*)ident(op) operator(=) operator(&()ident(OutboundPages)operator([)integer(0)operator(]\);) + + comment(// The nasty cast to (char*\) is needed because Windows is brain-dead.) + pre_type(int) ident(s) operator(=) ident(sendto) operator(()ident(sd)operator(,) operator(()pre_type(char)operator(*\))ident(op)operator(->)ident(Buffer)operator(,) ident(op)operator(->)ident(Length)operator(,) integer(0)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&()ident(op)operator(->)ident(From)operator(\),) reserved(sizeof)operator(()ident(op)operator(->)ident(From)operator(\)\);) + pre_type(int) ident(e) operator(=) ident(errno)operator(;) + + ident(OutboundDataSize) operator(-=) ident(op)operator(->)ident(Length)operator(;) + ident(op)operator(->)ident(Free)operator((\);) + ident(OutboundPages)operator(.)ident(pop_front)operator((\);) + + reserved(if) operator(()ident(s) operator(==) ident(SOCKET_ERROR)operator(\)) operator({) + preprocessor(#ifdef) ident(OS_UNIX) + reserved(if) operator((()ident(e) operator(!=) ident(EINPROGRESS)operator(\)) operator(&&) operator(()ident(e) operator(!=) ident(EWOULDBLOCK)operator(\)) operator(&&) operator(()ident(e) operator(!=) ident(EINTR)operator(\)\)) operator({) + preprocessor(#endif) + preprocessor(#ifdef) ident(OS_WIN32) + reserved(if) operator((()ident(e) operator(!=) ident(WSAEINPROGRESS)operator(\)) operator(&&) operator(()ident(e) operator(!=) ident(WSAEWOULDBLOCK)operator(\)\)) operator({) + preprocessor(#endif) + ident(Close)operator((\);) + reserved(break)operator(;) + operator(}) + operator(}) + operator(}) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) operator(()ident(SelectForWrite)operator((\)) operator(?) ident(EPOLLOUT) operator(:) integer(0)operator(\)\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/********************************** +DatagramDescriptor::SelectForWrite +**********************************/) + +pre_type(bool) ident(DatagramDescriptor)operator(::)ident(SelectForWrite)operator((\)) +operator({) + comment(/* Changed 15Nov07, per bug report by Mark Zvillius. + * The outbound data size will be zero if there are zero-length outbound packets, + * so we now select writable in case the outbound page buffer is not empty. + * Note that the superclass ShouldDelete method still checks for outbound data size, + * which may be wrong. + */) + comment(//return (GetOutboundDataSize(\) > 0\); (Original\)) + reserved(return) operator(()ident(OutboundPages)operator(.)ident(size)operator((\)) operator(>) integer(0)operator(\);) +operator(}) + + +comment(/************************************ +DatagramDescriptor::SendOutboundData +************************************/) + +pre_type(int) ident(DatagramDescriptor)operator(::)ident(SendOutboundData) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(\)) +operator({) + comment(// This is an exact clone of ConnectionDescriptor::SendOutboundData.) + comment(// That means it needs to move to a common ancestor.) + + reserved(if) operator(()ident(IsCloseScheduled)operator((\)\)) + comment(//if (bCloseNow || bCloseAfterWriting\)) + reserved(return) integer(0)operator(;) + + reserved(if) operator((!)ident(data) operator(&&) operator(()ident(length) operator(>) integer(0)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad outbound data)delimiter(")>operator(\);) + pre_type(char) operator(*)ident(buffer) operator(=) operator(()pre_type(char) operator(*\)) ident(malloc) operator(()ident(length) operator(+) integer(1)operator(\);) + reserved(if) operator((!)ident(buffer)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no allocation for outbound data)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(buffer)operator(,) ident(data)operator(,) ident(length)operator(\);) + ident(buffer) operator([)ident(length)operator(]) operator(=) integer(0)operator(;) + ident(OutboundPages)operator(.)ident(push_back) operator(()ident(OutboundPage) operator(()ident(buffer)operator(,) ident(length)operator(,) ident(ReturnAddress)operator(\)\);) + ident(OutboundDataSize) operator(+=) ident(length)operator(;) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) ident(EPOLLOUT)operator(\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + reserved(return) ident(length)operator(;) +operator(}) + + +comment(/**************************************** +DatagramDescriptor::SendOutboundDatagram +****************************************/) + +pre_type(int) ident(DatagramDescriptor)operator(::)ident(SendOutboundDatagram) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(,) directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + comment(// This is an exact clone of ConnectionDescriptor::SendOutboundData.) + comment(// That means it needs to move to a common ancestor.) + comment(// TODO: Refactor this so there's no overlap with SendOutboundData.) + + reserved(if) operator(()ident(IsCloseScheduled)operator((\)\)) + comment(//if (bCloseNow || bCloseAfterWriting\)) + reserved(return) integer(0)operator(;) + + reserved(if) operator((!)ident(address) operator(||) operator(!*)ident(address) operator(||) operator(!)ident(port)operator(\)) + reserved(return) integer(0)operator(;) + + ident(sockaddr_in) ident(pin)operator(;) + pre_type(unsigned) pre_type(long) ident(HostAddr)operator(;) + + ident(HostAddr) operator(=) ident(inet_addr) operator(()ident(address)operator(\);) + reserved(if) operator(()ident(HostAddr) operator(==) ident(INADDR_NONE)operator(\)) operator({) + comment(// The nasty cast to (char*\) is because Windows is brain-dead.) + ident(hostent) operator(*)ident(hp) operator(=) ident(gethostbyname) operator((()pre_type(char)operator(*\))ident(address)operator(\);) + reserved(if) operator((!)ident(hp)operator(\)) + reserved(return) integer(0)operator(;) + ident(HostAddr) operator(=) operator((()ident(in_addr)operator(*\)()ident(hp)operator(->)ident(h_addr)operator(\)\)->)ident(s_addr)operator(;) + operator(}) + + ident(memset) operator((&)ident(pin)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(pin)operator(\)\);) + ident(pin)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(pin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(HostAddr)operator(;) + ident(pin)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + + + + reserved(if) operator((!)ident(data) operator(&&) operator(()ident(length) operator(>) integer(0)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad outbound data)delimiter(")>operator(\);) + pre_type(char) operator(*)ident(buffer) operator(=) operator(()pre_type(char) operator(*\)) ident(malloc) operator(()ident(length) operator(+) integer(1)operator(\);) + reserved(if) operator((!)ident(buffer)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no allocation for outbound data)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(buffer)operator(,) ident(data)operator(,) ident(length)operator(\);) + ident(buffer) operator([)ident(length)operator(]) operator(=) integer(0)operator(;) + ident(OutboundPages)operator(.)ident(push_back) operator(()ident(OutboundPage) operator(()ident(buffer)operator(,) ident(length)operator(,) ident(pin)operator(\)\);) + ident(OutboundDataSize) operator(+=) ident(length)operator(;) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) ident(EPOLLOUT)operator(\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + reserved(return) ident(length)operator(;) +operator(}) + + +comment(/**************************************** +STATIC: DatagramDescriptor::SendDatagram +****************************************/) + +pre_type(int) ident(DatagramDescriptor)operator(::)ident(SendDatagram) operator(()directive(const) pre_type(char) operator(*)ident(binding)operator(,) directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(,) directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(DatagramDescriptor) operator(*)ident(dd) operator(=) reserved(dynamic_cast) operator(<)ident(DatagramDescriptor)operator(*>) operator(()ident(Bindable_t)operator(::)ident(GetObject) operator(()ident(binding)operator(\)\);) + reserved(if) operator(()ident(dd)operator(\)) + reserved(return) ident(dd)operator(->)ident(SendOutboundDatagram) operator(()ident(data)operator(,) ident(length)operator(,) ident(address)operator(,) ident(port)operator(\);) + reserved(else) + reserved(return) operator(-)integer(1)operator(;) +operator(}) + + +comment(/********************************* +ConnectionDescriptor::GetPeername +*********************************/) + +pre_type(bool) ident(ConnectionDescriptor)operator(::)ident(GetPeername) operator(()reserved(struct) ident(sockaddr) operator(*)ident(s)operator(\)) +operator({) + pre_type(bool) ident(ok) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(s)operator(\)) operator({) + ident(socklen_t) ident(len) operator(=) reserved(sizeof)operator((*)ident(s)operator(\);) + pre_type(int) ident(gp) operator(=) ident(getpeername) operator(()ident(GetSocket)operator((\),) ident(s)operator(,) operator(&)ident(len)operator(\);) + reserved(if) operator(()ident(gp) operator(==) integer(0)operator(\)) + ident(ok) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(return) ident(ok)operator(;) +operator(}) + +comment(/********************************* +ConnectionDescriptor::GetSockname +*********************************/) + +pre_type(bool) ident(ConnectionDescriptor)operator(::)ident(GetSockname) operator(()reserved(struct) ident(sockaddr) operator(*)ident(s)operator(\)) +operator({) + pre_type(bool) ident(ok) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(s)operator(\)) operator({) + ident(socklen_t) ident(len) operator(=) reserved(sizeof)operator((*)ident(s)operator(\);) + pre_type(int) ident(gp) operator(=) ident(getsockname) operator(()ident(GetSocket)operator((\),) ident(s)operator(,) operator(&)ident(len)operator(\);) + reserved(if) operator(()ident(gp) operator(==) integer(0)operator(\)) + ident(ok) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(return) ident(ok)operator(;) +operator(}) + + +comment(/********************************************** +ConnectionDescriptor::GetCommInactivityTimeout +**********************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(GetCommInactivityTimeout) operator(()pre_type(int) operator(*)ident(value)operator(\)) +operator({) + reserved(if) operator(()ident(value)operator(\)) operator({) + operator(*)ident(value) operator(=) ident(InactivityTimeout)operator(;) + reserved(return) integer(1)operator(;) + operator(}) + reserved(else) operator({) + comment(// TODO, extended logging, got bad parameter.) + reserved(return) integer(0)operator(;) + operator(}) +operator(}) + + +comment(/********************************************** +ConnectionDescriptor::SetCommInactivityTimeout +**********************************************/) + +pre_type(int) ident(ConnectionDescriptor)operator(::)ident(SetCommInactivityTimeout) operator(()pre_type(int) operator(*)ident(value)operator(\)) +operator({) + pre_type(int) ident(out) operator(=) integer(0)operator(;) + + reserved(if) operator(()ident(value)operator(\)) operator({) + reserved(if) operator(((*)ident(value)operator(==)integer(0)operator(\)) operator(||) operator((*)ident(value) operator(>=) integer(2)operator(\)\)) operator({) + comment(// Replace the value and send the old one back to the caller.) + pre_type(int) ident(v) operator(=) operator(*)ident(value)operator(;) + operator(*)ident(value) operator(=) ident(InactivityTimeout)operator(;) + ident(InactivityTimeout) operator(=) ident(v)operator(;) + ident(out) operator(=) integer(1)operator(;) + operator(}) + reserved(else) operator({) + comment(// TODO, extended logging, got bad value.) + operator(}) + operator(}) + reserved(else) operator({) + comment(// TODO, extended logging, got bad parameter.) + operator(}) + + reserved(return) ident(out)operator(;) +operator(}) + +comment(/******************************* +DatagramDescriptor::GetPeername +*******************************/) + +pre_type(bool) ident(DatagramDescriptor)operator(::)ident(GetPeername) operator(()reserved(struct) ident(sockaddr) operator(*)ident(s)operator(\)) +operator({) + pre_type(bool) ident(ok) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(s)operator(\)) operator({) + ident(memset) operator(()ident(s)operator(,) integer(0)operator(,) reserved(sizeof)operator(()reserved(struct) ident(sockaddr)operator(\)\);) + ident(memcpy) operator(()ident(s)operator(,) operator(&)ident(ReturnAddress)operator(,) reserved(sizeof)operator(()ident(ReturnAddress)operator(\)\);) + ident(ok) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(return) ident(ok)operator(;) +operator(}) + +comment(/******************************* +DatagramDescriptor::GetSockname +*******************************/) + +pre_type(bool) ident(DatagramDescriptor)operator(::)ident(GetSockname) operator(()reserved(struct) ident(sockaddr) operator(*)ident(s)operator(\)) +operator({) + pre_type(bool) ident(ok) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(s)operator(\)) operator({) + ident(socklen_t) ident(len) operator(=) reserved(sizeof)operator((*)ident(s)operator(\);) + pre_type(int) ident(gp) operator(=) ident(getsockname) operator(()ident(GetSocket)operator((\),) ident(s)operator(,) operator(&)ident(len)operator(\);) + reserved(if) operator(()ident(gp) operator(==) integer(0)operator(\)) + ident(ok) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(return) ident(ok)operator(;) +operator(}) + + + +comment(/******************************************** +DatagramDescriptor::GetCommInactivityTimeout +********************************************/) + +pre_type(int) ident(DatagramDescriptor)operator(::)ident(GetCommInactivityTimeout) operator(()pre_type(int) operator(*)ident(value)operator(\)) +operator({) + reserved(if) operator(()ident(value)operator(\)) operator({) + operator(*)ident(value) operator(=) ident(InactivityTimeout)operator(;) + reserved(return) integer(1)operator(;) + operator(}) + reserved(else) operator({) + comment(// TODO, extended logging, got bad parameter.) + reserved(return) integer(0)operator(;) + operator(}) +operator(}) + +comment(/******************************************** +DatagramDescriptor::SetCommInactivityTimeout +********************************************/) + +pre_type(int) ident(DatagramDescriptor)operator(::)ident(SetCommInactivityTimeout) operator(()pre_type(int) operator(*)ident(value)operator(\)) +operator({) + pre_type(int) ident(out) operator(=) integer(0)operator(;) + + reserved(if) operator(()ident(value)operator(\)) operator({) + reserved(if) operator(((*)ident(value)operator(==)integer(0)operator(\)) operator(||) operator((*)ident(value) operator(>=) integer(2)operator(\)\)) operator({) + comment(// Replace the value and send the old one back to the caller.) + pre_type(int) ident(v) operator(=) operator(*)ident(value)operator(;) + operator(*)ident(value) operator(=) ident(InactivityTimeout)operator(;) + ident(InactivityTimeout) operator(=) ident(v)operator(;) + ident(out) operator(=) integer(1)operator(;) + operator(}) + reserved(else) operator({) + comment(// TODO, extended logging, got bad value.) + operator(}) + operator(}) + reserved(else) operator({) + comment(// TODO, extended logging, got bad parameter.) + operator(}) + + reserved(return) ident(out)operator(;) +operator(}) + +comment(/***************************************************************************** + +$Id$ + +File: em.cpp +Date: 06Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +comment(// THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY.) +comment(//#ifdef OS_UNIX) + + +preprocessor(#include) include("project.h") + +comment(// Keep a global variable floating around) +comment(// with the current loop time as set by the Event Machine.) +comment(// This avoids the need for frequent expensive calls to time(NULL\);) +ident(time_t) ident(gCurrentLoopTime)operator(;) + +preprocessor(#ifdef) ident(OS_WIN32) +pre_type(unsigned) ident(gTickCountTickover)operator(;) +pre_type(unsigned) ident(gLastTickCount)operator(;) +preprocessor(#endif) + + +comment(/* The numer of max outstanding timers was once a const enum defined in em.h. + * Now we define it here so that users can change its value if necessary. + */) +directive(static) pre_type(int) ident(MaxOutstandingTimers) operator(=) integer(1000)operator(;) + + +comment(/* Internal helper to convert strings to internet addresses. IPv6-aware. + * Not reentrant or threadsafe, optimized for speed. + */) +directive(static) reserved(struct) ident(sockaddr) operator(*)ident(name2address) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(,) pre_type(int) operator(*)ident(family)operator(,) pre_type(int) operator(*)ident(bind_size)operator(\);) + + +comment(/*************************************** +STATIC EventMachine_t::SetMaxTimerCount +***************************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(SetMaxTimerCount) operator(()pre_type(int) ident(count)operator(\)) +operator({) + comment(/* Allow a user to increase the maximum number of outstanding timers. + * If this gets "too high" (a metric that is of course platform dependent\), + * bad things will happen like performance problems and possible overuse + * of memory. + * The actual timer mechanism is very efficient so it's hard to know what + * the practical max, but 100,000 shouldn't be too problematical. + */) + reserved(if) operator(()ident(count) operator(<) integer(100)operator(\)) + ident(count) operator(=) integer(100)operator(;) + ident(MaxOutstandingTimers) operator(=) ident(count)operator(;) +operator(}) + + + +comment(/****************************** +EventMachine_t::EventMachine_t +******************************/) + +ident(EventMachine_t)operator(::)ident(EventMachine_t) operator(()directive(void) operator((*)ident(event_callback)operator(\)()directive(const) pre_type(char)operator(*,) pre_type(int)operator(,) directive(const) pre_type(char)operator(*,) pre_type(int)operator(\)\):) + ident(EventCallback) operator(()ident(event_callback)operator(\),) + ident(NextHeartbeatTime) operator(()integer(0)operator(\),) + ident(LoopBreakerReader) operator((-)integer(1)operator(\),) + ident(LoopBreakerWriter) operator((-)integer(1)operator(\),) + ident(bEpoll) operator(()pre_constant(false)operator(\),) + ident(bKqueue) operator(()pre_constant(false)operator(\),) + ident(epfd) operator((-)integer(1)operator(\)) +operator({) + comment(// Default time-slice is just smaller than one hundred mills.) + ident(Quantum)operator(.)ident(tv_sec) operator(=) integer(0)operator(;) + ident(Quantum)operator(.)ident(tv_usec) operator(=) integer(90000)operator(;) + + ident(gTerminateSignalReceived) operator(=) pre_constant(false)operator(;) + comment(// Make sure the current loop time is sane, in case we do any initializations of) + comment(// objects before we start running.) + ident(gCurrentLoopTime) operator(=) ident(time)operator(()pre_constant(NULL)operator(\);) + + comment(/* We initialize the network library here (only on Windows of course\) + * and initialize "loop breakers." Our destructor also does some network-level + * cleanup. There's thus an implicit assumption that any given instance of EventMachine_t + * will only call ::Run once. Is that a good assumption? Should we move some of these + * inits and de-inits into ::Run? + */) + preprocessor(#ifdef) ident(OS_WIN32) + ident(WSADATA) ident(w)operator(;) + ident(WSAStartup) operator(()ident(MAKEWORD) operator(()integer(1)operator(,) integer(1)operator(\),) operator(&)ident(w)operator(\);) + preprocessor(#endif) + + ident(_InitializeLoopBreaker)operator((\);) +operator(}) + + +comment(/******************************* +EventMachine_t::~EventMachine_t +*******************************/) + +ident(EventMachine_t)operator(::~)ident(EventMachine_t)operator((\)) +operator({) + comment(// Run down descriptors) + ident(size_t) ident(i)operator(;) + reserved(for) operator(()ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) ident(NewDescriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) + reserved(delete) ident(NewDescriptors)operator([)ident(i)operator(];) + reserved(for) operator(()ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) + reserved(delete) ident(Descriptors)operator([)ident(i)operator(];) + + ident(close) operator(()ident(LoopBreakerReader)operator(\);) + ident(close) operator(()ident(LoopBreakerWriter)operator(\);) + + reserved(if) operator(()ident(epfd) operator(!=) operator(-)integer(1)operator(\)) + ident(close) operator(()ident(epfd)operator(\);) + reserved(if) operator(()ident(kqfd) operator(!=) operator(-)integer(1)operator(\)) + ident(close) operator(()ident(kqfd)operator(\);) +operator(}) + + +comment(/************************* +EventMachine_t::_UseEpoll +*************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_UseEpoll)operator((\)) +operator({) + comment(/* Temporary. + * Use an internal flag to switch in epoll-based functionality until we determine + * how it should be integrated properly and the extent of the required changes. + * A permanent solution needs to allow the integration of additional technologies, + * like kqueue and Solaris's events. + */) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(bEpoll) operator(=) pre_constant(true)operator(;) + preprocessor(#endif) +operator(}) + +comment(/************************** +EventMachine_t::_UseKqueue +**************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_UseKqueue)operator((\)) +operator({) + comment(/* Temporary. + * See comments under _UseEpoll. + */) + + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(bKqueue) operator(=) pre_constant(true)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/**************************** +EventMachine_t::ScheduleHalt +****************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(ScheduleHalt)operator((\)) +operator({) + comment(/* This is how we stop the machine. + * This can be called by clients. Signal handlers will probably + * set the global flag. + * For now this means there can only be one EventMachine ever running at a time. + * + * IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here, + * because it may be called from signal handlers invoked from code that we don't + * control. At this writing (20Sep06\), EM does NOT install any signal handlers of + * its own. + * + * We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens? + * The answer is to call evma_stop_machine, which calls here, from a SIGINT handler. + */) + ident(gTerminateSignalReceived) operator(=) pre_constant(true)operator(;) +operator(}) + + + +comment(/******************************* +EventMachine_t::SetTimerQuantum +*******************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(SetTimerQuantum) operator(()pre_type(int) ident(interval)operator(\)) +operator({) + comment(/* We get a timer-quantum expressed in milliseconds. + * Don't set a quantum smaller than 5 or larger than 2500. + */) + + reserved(if) operator((()ident(interval) operator(<) integer(5)operator(\)) operator(||) operator(()ident(interval) operator(>) integer(2500)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(invalid timer-quantum)delimiter(")>operator(\);) + + ident(Quantum)operator(.)ident(tv_sec) operator(=) ident(interval) operator(/) integer(1000)operator(;) + ident(Quantum)operator(.)ident(tv_usec) operator(=) operator(()ident(interval) operator(%) integer(1000)operator(\)) operator(*) integer(1000)operator(;) +operator(}) + + +comment(/************************************* +(STATIC\) EventMachine_t::SetuidString +*************************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(SetuidString) operator(()directive(const) pre_type(char) operator(*)ident(username)operator(\)) +operator({) + comment(/* This method takes a caller-supplied username and tries to setuid + * to that user. There is no meaningful implementation (and no error\) + * on Windows. On Unix, a failure to setuid the caller-supplied string + * causes a fatal abort, because presumably the program is calling here + * in order to fulfill a security requirement. If we fail silently, + * the user may continue to run with too much privilege. + * + * TODO, we need to decide on and document a way of generating C++ level errors + * that can be wrapped in documented Ruby exceptions, so users can catch + * and handle them. And distinguish it from errors that we WON'T let the Ruby + * user catch (like security-violations and resource-overallocation\). + * A setuid failure here would be in the latter category. + */) + + preprocessor(#ifdef) ident(OS_UNIX) + reserved(if) operator((!)ident(username) operator(||) operator(!*)ident(username)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(setuid_string failed: no username specified)delimiter(")>operator(\);) + + reserved(struct) ident(passwd) operator(*)ident(p) operator(=) ident(getpwnam) operator(()ident(username)operator(\);) + reserved(if) operator((!)ident(p)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(setuid_string failed: unknown username)delimiter(")>operator(\);) + + reserved(if) operator(()ident(setuid) operator(()ident(p)operator(->)ident(pw_uid)operator(\)) operator(!=) integer(0)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(setuid_string failed: no setuid)delimiter(")>operator(\);) + + comment(// Success.) + preprocessor(#endif) +operator(}) + + +comment(/**************************************** +(STATIC\) EventMachine_t::SetRlimitNofile +****************************************/) + +pre_type(int) ident(EventMachine_t)operator(::)ident(SetRlimitNofile) operator(()pre_type(int) ident(nofiles)operator(\)) +operator({) + preprocessor(#ifdef) ident(OS_UNIX) + reserved(struct) ident(rlimit) ident(rlim)operator(;) + ident(getrlimit) operator(()ident(RLIMIT_NOFILE)operator(,) operator(&)ident(rlim)operator(\);) + reserved(if) operator(()ident(nofiles) operator(>=) integer(0)operator(\)) operator({) + ident(rlim)operator(.)ident(rlim_cur) operator(=) ident(nofiles)operator(;) + reserved(if) operator(()ident(nofiles) operator(>) ident(rlim)operator(.)ident(rlim_max)operator(\)) + ident(rlim)operator(.)ident(rlim_max) operator(=) ident(nofiles)operator(;) + ident(setrlimit) operator(()ident(RLIMIT_NOFILE)operator(,) operator(&)ident(rlim)operator(\);) + comment(// ignore the error return, for now at least.) + comment(// TODO, emit an error message someday when we have proper debug levels.) + operator(}) + ident(getrlimit) operator(()ident(RLIMIT_NOFILE)operator(,) operator(&)ident(rlim)operator(\);) + reserved(return) ident(rlim)operator(.)ident(rlim_cur)operator(;) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + comment(// No meaningful implementation on Windows.) + reserved(return) integer(0)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/********************************* +EventMachine_t::SignalLoopBreaker +*********************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(SignalLoopBreaker)operator((\)) +operator({) + preprocessor(#ifdef) ident(OS_UNIX) + ident(write) operator(()ident(LoopBreakerWriter)operator(,) string<delimiter(")delimiter(")>operator(,) integer(1)operator(\);) + preprocessor(#endif) + preprocessor(#ifdef) ident(OS_WIN32) + ident(sendto) operator(()ident(LoopBreakerReader)operator(,) string<delimiter(")delimiter(")>operator(,) integer(0)operator(,) integer(0)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&()ident(LoopBreakerTarget)operator(\),) reserved(sizeof)operator(()ident(LoopBreakerTarget)operator(\)\);) + preprocessor(#endif) +operator(}) + + +comment(/************************************** +EventMachine_t::_InitializeLoopBreaker +**************************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_InitializeLoopBreaker)operator((\)) +operator({) + comment(/* A "loop-breaker" is a socket-descriptor that we can write to in order + * to break the main select loop. Primarily useful for things running on + * threads other than the main EM thread, so they can trigger processing + * of events that arise exogenously to the EM. + * Keep the loop-breaker pipe out of the main descriptor set, otherwise + * its events will get passed on to user code. + */) + + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(fd)operator([)integer(2)operator(];) + reserved(if) operator(()ident(pipe) operator(()ident(fd)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no loop breaker)delimiter(")>operator(\);) + + ident(LoopBreakerWriter) operator(=) ident(fd)operator([)integer(1)operator(];) + ident(LoopBreakerReader) operator(=) ident(fd)operator([)integer(0)operator(];) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + pre_type(int) ident(sd) operator(=) ident(socket) operator(()ident(AF_INET)operator(,) ident(SOCK_DGRAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no loop breaker socket)delimiter(")>operator(\);) + ident(SetSocketNonblocking) operator(()ident(sd)operator(\);) + + ident(memset) operator((&)ident(LoopBreakerTarget)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(LoopBreakerTarget)operator(\)\);) + ident(LoopBreakerTarget)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(LoopBreakerTarget)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(inet_addr) operator(()string<delimiter(")content(127.0.0.1)delimiter(")>operator(\);) + + ident(srand) operator((()pre_type(int)operator(\))ident(time)operator(()pre_constant(NULL)operator(\)\);) + pre_type(int) ident(i)operator(;) + reserved(for) operator(()ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) integer(100)operator(;) ident(i)operator(++\)) operator({) + pre_type(int) ident(r) operator(=) operator(()ident(rand)operator((\)) operator(%) integer(10000)operator(\)) operator(+) integer(20000)operator(;) + ident(LoopBreakerTarget)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(r)operator(\);) + reserved(if) operator(()ident(bind) operator(()ident(sd)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(LoopBreakerTarget)operator(,) reserved(sizeof)operator(()ident(LoopBreakerTarget)operator(\)\)) operator(==) integer(0)operator(\)) + reserved(break)operator(;) + operator(}) + + reserved(if) operator(()ident(i) operator(==) integer(100)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no loop breaker)delimiter(")>operator(\);) + ident(LoopBreakerReader) operator(=) ident(sd)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/******************* +EventMachine_t::Run +*******************/) + +directive(void) ident(EventMachine_t)operator(::)ident(Run)operator((\)) +operator({) + preprocessor(#ifdef) ident(OS_WIN32) + ident(HookControlC) operator(()pre_constant(true)operator(\);) + preprocessor(#endif) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + reserved(if) operator(()ident(bEpoll)operator(\)) operator({) + ident(epfd) operator(=) ident(epoll_create) operator(()ident(MaxEpollDescriptors)operator(\);) + reserved(if) operator(()ident(epfd) operator(==) operator(-)integer(1)operator(\)) operator({) + pre_type(char) ident(buf)operator([)integer(200)operator(];) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(unable to create epoll descriptor: %s)delimiter(")>operator(,) ident(strerror)operator(()ident(errno)operator(\)\);) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(buf)operator(\);) + operator(}) + pre_type(int) ident(cloexec) operator(=) ident(fcntl) operator(()ident(epfd)operator(,) ident(F_GETFD)operator(,) integer(0)operator(\);) + ident(assert) operator(()ident(cloexec) operator(>=) integer(0)operator(\);) + ident(cloexec) operator(|=) ident(FD_CLOEXEC)operator(;) + ident(fcntl) operator(()ident(epfd)operator(,) ident(F_SETFD)operator(,) ident(cloexec)operator(\);) + + ident(assert) operator(()ident(LoopBreakerReader) operator(>=) integer(0)operator(\);) + ident(LoopbreakDescriptor) operator(*)ident(ld) operator(=) reserved(new) ident(LoopbreakDescriptor) operator(()ident(LoopBreakerReader)operator(,) local_variable(this)operator(\);) + ident(assert) operator(()ident(ld)operator(\);) + ident(Add) operator(()ident(ld)operator(\);) + operator(}) + preprocessor(#endif) + + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(if) operator(()ident(bKqueue)operator(\)) operator({) + ident(kqfd) operator(=) ident(kqueue)operator((\);) + reserved(if) operator(()ident(kqfd) operator(==) operator(-)integer(1)operator(\)) operator({) + pre_type(char) ident(buf)operator([)integer(200)operator(];) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(unable to create kqueue descriptor: %s)delimiter(")>operator(,) ident(strerror)operator(()ident(errno)operator(\)\);) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(buf)operator(\);) + operator(}) + comment(// cloexec not needed. By definition, kqueues are not carried across forks.) + + ident(assert) operator(()ident(LoopBreakerReader) operator(>=) integer(0)operator(\);) + ident(LoopbreakDescriptor) operator(*)ident(ld) operator(=) reserved(new) ident(LoopbreakDescriptor) operator(()ident(LoopBreakerReader)operator(,) local_variable(this)operator(\);) + ident(assert) operator(()ident(ld)operator(\);) + ident(Add) operator(()ident(ld)operator(\);) + operator(}) + preprocessor(#endif) + + reserved(while) operator(()pre_constant(true)operator(\)) operator({) + ident(gCurrentLoopTime) operator(=) ident(time)operator(()pre_constant(NULL)operator(\);) + reserved(if) operator((!)ident(_RunTimers)operator((\)\)) + reserved(break)operator(;) + + comment(/* _Add must precede _Modify because the same descriptor might + * be on both lists during the same pass through the machine, + * and to modify a descriptor before adding it would fail. + */) + ident(_AddNewDescriptors)operator((\);) + ident(_ModifyDescriptors)operator((\);) + + reserved(if) operator((!)ident(_RunOnce)operator((\)\)) + reserved(break)operator(;) + reserved(if) operator(()ident(gTerminateSignalReceived)operator(\)) + reserved(break)operator(;) + operator(}) + + preprocessor(#ifdef) ident(OS_WIN32) + ident(HookControlC) operator(()pre_constant(false)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/************************ +EventMachine_t::_RunOnce +************************/) + +pre_type(bool) ident(EventMachine_t)operator(::)ident(_RunOnce)operator((\)) +operator({) + reserved(if) operator(()ident(bEpoll)operator(\)) + reserved(return) ident(_RunEpollOnce)operator((\);) + reserved(else) reserved(if) operator(()ident(bKqueue)operator(\)) + reserved(return) ident(_RunKqueueOnce)operator((\);) + reserved(else) + reserved(return) ident(_RunSelectOnce)operator((\);) +operator(}) + + + +comment(/***************************** +EventMachine_t::_RunEpollOnce +*****************************/) + +pre_type(bool) ident(EventMachine_t)operator(::)ident(_RunEpollOnce)operator((\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(assert) operator(()ident(epfd) operator(!=) operator(-)integer(1)operator(\);) + reserved(struct) ident(epoll_event) ident(ev) operator([)ident(MaxEpollDescriptors)operator(];) + pre_type(int) ident(s)operator(;) + + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(TRAP_BEG)operator(;) + preprocessor(#endif) + ident(s) operator(=) ident(epoll_wait) operator(()ident(epfd)operator(,) ident(ev)operator(,) ident(MaxEpollDescriptors)operator(,) integer(50)operator(\);) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(TRAP_END)operator(;) + preprocessor(#endif) + + reserved(if) operator(()ident(s) operator(>) integer(0)operator(\)) operator({) + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(s)operator(;) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) operator(()ident(EventableDescriptor)operator(*\)) ident(ev)operator([)ident(i)operator(])operator(.)ident(data)operator(.)ident(ptr)operator(;) + + reserved(if) operator(()ident(ev)operator([)ident(i)operator(])operator(.)ident(events) operator(&) operator(()ident(EPOLLERR) operator(|) ident(EPOLLHUP)operator(\)\)) + ident(ed)operator(->)ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + reserved(if) operator(()ident(ev)operator([)ident(i)operator(])operator(.)ident(events) operator(&) ident(EPOLLIN)operator(\)) + ident(ed)operator(->)ident(Read)operator((\);) + reserved(if) operator(()ident(ev)operator([)ident(i)operator(])operator(.)ident(events) operator(&) ident(EPOLLOUT)operator(\)) operator({) + ident(ed)operator(->)ident(Write)operator((\);) + ident(epoll_ctl) operator(()ident(epfd)operator(,) ident(EPOLL_CTL_MOD)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(ed)operator(->)ident(GetEpollEvent)operator((\)\);) + comment(// Ignoring return value) + operator(}) + operator(}) + operator(}) + reserved(else) reserved(if) operator(()ident(s) operator(<) integer(0)operator(\)) operator({) + comment(// epoll_wait can fail on error in a handful of ways.) + comment(// If this happens, then wait for a little while to avoid busy-looping.) + comment(// If the error was EINTR, we probably caught SIGCHLD or something,) + comment(// so keep the wait short.) + ident(timeval) ident(tv) operator(=) operator({)integer(0)operator(,) operator((()ident(errno) operator(==) ident(EINTR)operator(\)) operator(?) integer(5) operator(:) integer(50)operator(\)) operator(*) integer(1000)operator(};) + ident(EmSelect) operator(()integer(0)operator(,) pre_constant(NULL)operator(,) pre_constant(NULL)operator(,) pre_constant(NULL)operator(,) operator(&)ident(tv)operator(\);) + operator(}) + + operator({) comment(// cleanup dying sockets) + comment(// vector::pop_back works in constant time.) + comment(// TODO, rip this out and only delete the descriptors we know have died,) + comment(// rather than traversing the whole list.) + comment(// Modified 05Jan08 per suggestions by Chris Heath. It's possible that) + comment(// an EventableDescriptor will have a descriptor value of -1. That will) + comment(// happen if EventableDescriptor::Close was called on it. In that case,) + comment(// don't call epoll_ctl to remove the socket's filters from the epoll set.) + comment(// According to the epoll docs, this happens automatically when the) + comment(// descriptor is closed anyway. This is different from the case where) + comment(// the socket has already been closed but the descriptor in the ED object) + comment(// hasn't yet been set to INVALID_SOCKET.) + pre_type(int) ident(i)operator(,) ident(j)operator(;) + pre_type(int) ident(nSockets) operator(=) ident(Descriptors)operator(.)ident(size)operator((\);) + reserved(for) operator(()ident(i)operator(=)integer(0)operator(,) ident(j)operator(=)integer(0)operator(;) ident(i) operator(<) ident(nSockets)operator(;) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + reserved(if) operator(()ident(ed)operator(->)ident(ShouldDelete)operator((\)\)) operator({) + reserved(if) operator(()ident(ed)operator(->)ident(GetSocket)operator((\)) operator(!=) ident(INVALID_SOCKET)operator(\)) operator({) + ident(assert) operator(()ident(bEpoll)operator(\);) comment(// wouldn't be in this method otherwise.) + ident(assert) operator(()ident(epfd) operator(!=) operator(-)integer(1)operator(\);) + pre_type(int) ident(e) operator(=) ident(epoll_ctl) operator(()ident(epfd)operator(,) ident(EPOLL_CTL_DEL)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(ed)operator(->)ident(GetEpollEvent)operator((\)\);) + comment(// ENOENT or EBADF are not errors because the socket may be already closed when we get here.) + reserved(if) operator(()ident(e) operator(&&) operator(()ident(errno) operator(!=) ident(ENOENT)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(EBADF)operator(\)\)) operator({) + pre_type(char) ident(buf) operator([)integer(200)operator(];) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(unable to delete epoll event: %s)delimiter(")>operator(,) ident(strerror)operator(()ident(errno)operator(\)\);) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(buf)operator(\);) + operator(}) + operator(}) + + ident(ModifiedDescriptors)operator(.)ident(erase) operator(()ident(ed)operator(\);) + reserved(delete) ident(ed)operator(;) + operator(}) + reserved(else) + ident(Descriptors) operator([)ident(j)operator(++]) operator(=) ident(ed)operator(;) + operator(}) + reserved(while) operator((()ident(size_t)operator(\))ident(j) operator(<) ident(Descriptors)operator(.)ident(size)operator((\)\)) + ident(Descriptors)operator(.)ident(pop_back)operator((\);) + + operator(}) + + comment(// TODO, heartbeats.) + comment(// Added 14Sep07, its absence was noted by Brian Candler. But the comment was here, indicated) + comment(// that this got thought about and not done when EPOLL was originally written. Was there a reason) + comment(// not to do it, or was it an oversight? Certainly, running a heartbeat on 50,000 connections every) + comment(// two seconds can get to be a real bear, especially if all we're doing is timing out dead ones.) + comment(// Maybe there's a better way to do this. (Or maybe it's not that expensive after all.\)) + comment(//) + operator({) comment(// dispatch heartbeats) + reserved(if) operator(()ident(gCurrentLoopTime) operator(>=) ident(NextHeartbeatTime)operator(\)) operator({) + ident(NextHeartbeatTime) operator(=) ident(gCurrentLoopTime) operator(+) ident(HeartbeatInterval)operator(;) + + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + ident(ed)operator(->)ident(Heartbeat)operator((\);) + operator(}) + operator(}) + operator(}) + + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + reserved(if) operator((!)ident(rb_thread_alone)operator((\)\)) operator({) + ident(rb_thread_schedule)operator((\);) + operator(}) + preprocessor(#endif) + + reserved(return) pre_constant(true)operator(;) + preprocessor(#else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(epoll is not implemented on this platform)delimiter(")>operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/****************************** +EventMachine_t::_RunKqueueOnce +******************************/) + +pre_type(bool) ident(EventMachine_t)operator(::)ident(_RunKqueueOnce)operator((\)) +operator({) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(assert) operator(()ident(kqfd) operator(!=) operator(-)integer(1)operator(\);) + directive(const) pre_type(int) ident(maxKevents) operator(=) integer(2000)operator(;) + reserved(struct) ident(kevent) ident(Karray) operator([)ident(maxKevents)operator(];) + reserved(struct) ident(timespec) ident(ts) operator(=) operator({)integer(0)operator(,) integer(10000000)operator(};) comment(// Too frequent. Use blocking_region) + + pre_type(int) ident(k)operator(;) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(TRAP_BEG)operator(;) + preprocessor(#endif) + ident(k) operator(=) ident(kevent) operator(()ident(kqfd)operator(,) pre_constant(NULL)operator(,) integer(0)operator(,) ident(Karray)operator(,) ident(maxKevents)operator(,) operator(&)ident(ts)operator(\);) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + ident(TRAP_END)operator(;) + preprocessor(#endif) + reserved(struct) ident(kevent) operator(*)ident(ke) operator(=) ident(Karray)operator(;) + reserved(while) operator(()ident(k) operator(>) integer(0)operator(\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) operator(()ident(EventableDescriptor)operator(*\)) operator(()ident(ke)operator(->)ident(udata)operator(\);) + ident(assert) operator(()ident(ed)operator(\);) + + reserved(if) operator(()ident(ke)operator(->)ident(filter) operator(==) ident(EVFILT_READ)operator(\)) + ident(ed)operator(->)ident(Read)operator((\);) + reserved(else) reserved(if) operator(()ident(ke)operator(->)ident(filter) operator(==) ident(EVFILT_WRITE)operator(\)) + ident(ed)operator(->)ident(Write)operator((\);) + reserved(else) + ident(cerr) operator(<<) string<delimiter(")content(Discarding unknown kqueue event )delimiter(")> operator(<<) ident(ke)operator(->)ident(filter) operator(<<) ident(endl)operator(;) + + operator(--)ident(k)operator(;) + operator(++)ident(ke)operator(;) + operator(}) + + operator({) comment(// cleanup dying sockets) + comment(// vector::pop_back works in constant time.) + comment(// TODO, rip this out and only delete the descriptors we know have died,) + comment(// rather than traversing the whole list.) + comment(// In kqueue, closing a descriptor automatically removes its event filters.) + + pre_type(int) ident(i)operator(,) ident(j)operator(;) + pre_type(int) ident(nSockets) operator(=) ident(Descriptors)operator(.)ident(size)operator((\);) + reserved(for) operator(()ident(i)operator(=)integer(0)operator(,) ident(j)operator(=)integer(0)operator(;) ident(i) operator(<) ident(nSockets)operator(;) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + reserved(if) operator(()ident(ed)operator(->)ident(ShouldDelete)operator((\)\)) operator({) + ident(ModifiedDescriptors)operator(.)ident(erase) operator(()ident(ed)operator(\);) + reserved(delete) ident(ed)operator(;) + operator(}) + reserved(else) + ident(Descriptors) operator([)ident(j)operator(++]) operator(=) ident(ed)operator(;) + operator(}) + reserved(while) operator((()ident(size_t)operator(\))ident(j) operator(<) ident(Descriptors)operator(.)ident(size)operator((\)\)) + ident(Descriptors)operator(.)ident(pop_back)operator((\);) + + operator(}) + + operator({) comment(// dispatch heartbeats) + reserved(if) operator(()ident(gCurrentLoopTime) operator(>=) ident(NextHeartbeatTime)operator(\)) operator({) + ident(NextHeartbeatTime) operator(=) ident(gCurrentLoopTime) operator(+) ident(HeartbeatInterval)operator(;) + + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + ident(ed)operator(->)ident(Heartbeat)operator((\);) + operator(}) + operator(}) + operator(}) + + + comment(// TODO, replace this with rb_thread_blocking_region for 1.9 builds.) + preprocessor(#ifdef) ident(BUILD_FOR_RUBY) + reserved(if) operator((!)ident(rb_thread_alone)operator((\)\)) operator({) + ident(rb_thread_schedule)operator((\);) + operator(}) + preprocessor(#endif) + + reserved(return) pre_constant(true)operator(;) + preprocessor(#else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(kqueue is not implemented on this platform)delimiter(")>operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/********************************* +EventMachine_t::_ModifyEpollEvent +*********************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_ModifyEpollEvent) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + reserved(if) operator(()ident(bEpoll)operator(\)) operator({) + ident(assert) operator(()ident(epfd) operator(!=) operator(-)integer(1)operator(\);) + ident(assert) operator(()ident(ed)operator(\);) + pre_type(int) ident(e) operator(=) ident(epoll_ctl) operator(()ident(epfd)operator(,) ident(EPOLL_CTL_MOD)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(ed)operator(->)ident(GetEpollEvent)operator((\)\);) + reserved(if) operator(()ident(e)operator(\)) operator({) + pre_type(char) ident(buf) operator([)integer(200)operator(];) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(unable to modify epoll event: %s)delimiter(")>operator(,) ident(strerror)operator(()ident(errno)operator(\)\);) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(buf)operator(\);) + operator(}) + operator(}) + preprocessor(#endif) +operator(}) + + + +comment(/************************** +SelectData_t::SelectData_t +**************************/) + +ident(SelectData_t)operator(::)ident(SelectData_t)operator((\)) +operator({) + ident(maxsocket) operator(=) integer(0)operator(;) + ident(FD_ZERO) operator((&)ident(fdreads)operator(\);) + ident(FD_ZERO) operator((&)ident(fdwrites)operator(\);) +operator(}) + + +preprocessor(#ifdef) ident(BUILD_FOR_RUBY) +comment(/***************** +_SelectDataSelect +*****************/) + +preprocessor(#ifdef) ident(HAVE_TBR) +directive(static) ident(VALUE) ident(_SelectDataSelect) operator(()directive(void) operator(*)ident(v)operator(\)) +operator({) + ident(SelectData_t) operator(*)ident(sd) operator(=) operator(()ident(SelectData_t)operator(*\))ident(v)operator(;) + ident(sd)operator(->)ident(nSockets) operator(=) ident(select) operator(()ident(sd)operator(->)ident(maxsocket)operator(+)integer(1)operator(,) operator(&()ident(sd)operator(->)ident(fdreads)operator(\),) operator(&()ident(sd)operator(->)ident(fdwrites)operator(\),) pre_constant(NULL)operator(,) operator(&()ident(sd)operator(->)ident(tv)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) +preprocessor(#endif) + +comment(/********************* +SelectData_t::_Select +*********************/) + +pre_type(int) ident(SelectData_t)operator(::)ident(_Select)operator((\)) +operator({) + preprocessor(#ifdef) ident(HAVE_TBR) + ident(rb_thread_blocking_region) operator(()ident(_SelectDataSelect)operator(,) operator(()directive(void)operator(*\))local_variable(this)operator(,) ident(RUBY_UBF_IO)operator(,) integer(0)operator(\);) + reserved(return) ident(nSockets)operator(;) + preprocessor(#endif) + + preprocessor(#ifndef) ident(HAVE_TBR) + reserved(return) ident(EmSelect) operator(()ident(maxsocket)operator(+)integer(1)operator(,) operator(&)ident(fdreads)operator(,) operator(&)ident(fdwrites)operator(,) pre_constant(NULL)operator(,) operator(&)ident(tv)operator(\);) + preprocessor(#endif) +operator(}) +preprocessor(#endif) + + + +comment(/****************************** +EventMachine_t::_RunSelectOnce +******************************/) + +pre_type(bool) ident(EventMachine_t)operator(::)ident(_RunSelectOnce)operator((\)) +operator({) + comment(// Crank the event machine once.) + comment(// If there are no descriptors to process, then sleep) + comment(// for a few hundred mills to avoid busy-looping.) + comment(// Return T/F to indicate whether we should continue.) + comment(// This is based on a select loop. Alternately provide epoll) + comment(// if we know we're running on a 2.6 kernel.) + comment(// epoll will be effective if we provide it as an alternative,) + comment(// however it has the same problem interoperating with Ruby) + comment(// threads that select does.) + + comment(//cerr << "X";) + + comment(/* This protection is now obsolete, because we will ALWAYS + * have at least one descriptor (the loop-breaker\) to read. + */) + comment(/* + if (Descriptors.size(\) == 0\) { + #ifdef OS_UNIX + timeval tv = {0, 200 * 1000}; + EmSelect (0, NULL, NULL, NULL, &tv\); + return true; + #endif + #ifdef OS_WIN32 + Sleep (200\); + return true; + #endif + } + */) + + ident(SelectData_t) ident(SelectData)operator(;) + comment(/* + fd_set fdreads, fdwrites; + FD_ZERO (&fdreads\); + FD_ZERO (&fdwrites\); + + int maxsocket = 0; + */) + + comment(// Always read the loop-breaker reader.) + comment(// Changed 23Aug06, provisionally implemented for Windows with a UDP socket) + comment(// running on localhost with a randomly-chosen port. (*Puke*\)) + comment(// Windows has a version of the Unix pipe(\) library function, but it doesn't) + comment(// give you back descriptors that are selectable.) + ident(FD_SET) operator(()ident(LoopBreakerReader)operator(,) operator(&()ident(SelectData)operator(.)ident(fdreads)operator(\)\);) + reserved(if) operator(()ident(SelectData)operator(.)ident(maxsocket) operator(<) ident(LoopBreakerReader)operator(\)) + ident(SelectData)operator(.)ident(maxsocket) operator(=) ident(LoopBreakerReader)operator(;) + + comment(// prepare the sockets for reading and writing) + ident(size_t) ident(i)operator(;) + reserved(for) operator(()ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + pre_type(int) ident(sd) operator(=) ident(ed)operator(->)ident(GetSocket)operator((\);) + ident(assert) operator(()ident(sd) operator(!=) ident(INVALID_SOCKET)operator(\);) + + reserved(if) operator(()ident(ed)operator(->)ident(SelectForRead)operator((\)\)) + ident(FD_SET) operator(()ident(sd)operator(,) operator(&()ident(SelectData)operator(.)ident(fdreads)operator(\)\);) + reserved(if) operator(()ident(ed)operator(->)ident(SelectForWrite)operator((\)\)) + ident(FD_SET) operator(()ident(sd)operator(,) operator(&()ident(SelectData)operator(.)ident(fdwrites)operator(\)\);) + + reserved(if) operator(()ident(SelectData)operator(.)ident(maxsocket) operator(<) ident(sd)operator(\)) + ident(SelectData)operator(.)ident(maxsocket) operator(=) ident(sd)operator(;) + operator(}) + + + operator({) comment(// read and write the sockets) + comment(//timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.) + comment(//timeval tv = Quantum;) + ident(SelectData)operator(.)ident(tv) operator(=) ident(Quantum)operator(;) + pre_type(int) ident(s) operator(=) ident(SelectData)operator(.)ident(_Select)operator((\);) + comment(//rb_thread_blocking_region(xxx,(void*\)&SelectData,RUBY_UBF_IO,0\);) + comment(//int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads\), &(SelectData.fdwrites\), NULL, &(SelectData.tv\)\);) + comment(//int s = SelectData.nSockets;) + reserved(if) operator(()ident(s) operator(>) integer(0)operator(\)) operator({) + comment(/* Changed 01Jun07. We used to handle the Loop-breaker right here. + * Now we do it AFTER all the regular descriptors. There's an + * incredibly important and subtle reason for this. Code on + * loop breakers is sometimes used to cause the reactor core to + * cycle (for example, to allow outbound network buffers to drain\). + * If a loop-breaker handler reschedules itself (say, after determining + * that the write buffers are still too full\), then it will execute + * IMMEDIATELY if _ReadLoopBreaker is done here instead of after + * the other descriptors are processed. That defeats the whole purpose. + */) + reserved(for) operator(()ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + pre_type(int) ident(sd) operator(=) ident(ed)operator(->)ident(GetSocket)operator((\);) + ident(assert) operator(()ident(sd) operator(!=) ident(INVALID_SOCKET)operator(\);) + + reserved(if) operator(()ident(FD_ISSET) operator(()ident(sd)operator(,) operator(&()ident(SelectData)operator(.)ident(fdwrites)operator(\)\)\)) + ident(ed)operator(->)ident(Write)operator((\);) + reserved(if) operator(()ident(FD_ISSET) operator(()ident(sd)operator(,) operator(&()ident(SelectData)operator(.)ident(fdreads)operator(\)\)\)) + ident(ed)operator(->)ident(Read)operator((\);) + operator(}) + + reserved(if) operator(()ident(FD_ISSET) operator(()ident(LoopBreakerReader)operator(,) operator(&()ident(SelectData)operator(.)ident(fdreads)operator(\)\)\)) + ident(_ReadLoopBreaker)operator((\);) + operator(}) + reserved(else) reserved(if) operator(()ident(s) operator(<) integer(0)operator(\)) operator({) + comment(// select can fail on error in a handful of ways.) + comment(// If this happens, then wait for a little while to avoid busy-looping.) + comment(// If the error was EINTR, we probably caught SIGCHLD or something,) + comment(// so keep the wait short.) + ident(timeval) ident(tv) operator(=) operator({)integer(0)operator(,) operator((()ident(errno) operator(==) ident(EINTR)operator(\)) operator(?) integer(5) operator(:) integer(50)operator(\)) operator(*) integer(1000)operator(};) + ident(EmSelect) operator(()integer(0)operator(,) pre_constant(NULL)operator(,) pre_constant(NULL)operator(,) pre_constant(NULL)operator(,) operator(&)ident(tv)operator(\);) + operator(}) + operator(}) + + + operator({) comment(// dispatch heartbeats) + reserved(if) operator(()ident(gCurrentLoopTime) operator(>=) ident(NextHeartbeatTime)operator(\)) operator({) + ident(NextHeartbeatTime) operator(=) ident(gCurrentLoopTime) operator(+) ident(HeartbeatInterval)operator(;) + + reserved(for) operator(()ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + ident(ed)operator(->)ident(Heartbeat)operator((\);) + operator(}) + operator(}) + operator(}) + + operator({) comment(// cleanup dying sockets) + comment(// vector::pop_back works in constant time.) + pre_type(int) ident(i)operator(,) ident(j)operator(;) + pre_type(int) ident(nSockets) operator(=) ident(Descriptors)operator(.)ident(size)operator((\);) + reserved(for) operator(()ident(i)operator(=)integer(0)operator(,) ident(j)operator(=)integer(0)operator(;) ident(i) operator(<) ident(nSockets)operator(;) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + reserved(if) operator(()ident(ed)operator(->)ident(ShouldDelete)operator((\)\)) + reserved(delete) ident(ed)operator(;) + reserved(else) + ident(Descriptors) operator([)ident(j)operator(++]) operator(=) ident(ed)operator(;) + operator(}) + reserved(while) operator((()ident(size_t)operator(\))ident(j) operator(<) ident(Descriptors)operator(.)ident(size)operator((\)\)) + ident(Descriptors)operator(.)ident(pop_back)operator((\);) + + operator(}) + + reserved(return) pre_constant(true)operator(;) +operator(}) + + +comment(/******************************** +EventMachine_t::_ReadLoopBreaker +********************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_ReadLoopBreaker)operator((\)) +operator({) + comment(/* The loop breaker has selected readable. + * Read it ONCE (it may block if we try to read it twice\) + * and send a loop-break event back to user code. + */) + pre_type(char) ident(buffer) operator([)integer(1024)operator(];) + ident(read) operator(()ident(LoopBreakerReader)operator(,) ident(buffer)operator(,) reserved(sizeof)operator(()ident(buffer)operator(\)\);) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()string<delimiter(")delimiter(")>operator(,) ident(EM_LOOPBREAK_SIGNAL)operator(,) string<delimiter(")delimiter(")>operator(,) integer(0)operator(\);) +operator(}) + + +comment(/************************** +EventMachine_t::_RunTimers +**************************/) + +pre_type(bool) ident(EventMachine_t)operator(::)ident(_RunTimers)operator((\)) +operator({) + comment(// These are caller-defined timer handlers.) + comment(// Return T/F to indicate whether we should continue the main loop.) + comment(// We rely on the fact that multimaps sort by their keys to avoid) + comment(// inspecting the whole list every time we come here.) + comment(// Just keep inspecting and processing the list head until we hit) + comment(// one that hasn't expired yet.) + + preprocessor(#ifdef) ident(OS_UNIX) + reserved(struct) ident(timeval) ident(tv)operator(;) + ident(gettimeofday) operator((&)ident(tv)operator(,) pre_constant(NULL)operator(\);) + ident(Int64) ident(now) operator(=) operator(((()ident(Int64)operator(\)()ident(tv)operator(.)ident(tv_sec)operator(\)\)) operator(*) integer(1000000LL)operator(\)) operator(+) operator((()ident(Int64)operator(\)()ident(tv)operator(.)ident(tv_usec)operator(\)\);) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + pre_type(unsigned) ident(tick) operator(=) ident(GetTickCount)operator((\);) + reserved(if) operator(()ident(tick) operator(<) ident(gLastTickCount)operator(\)) + ident(gTickCountTickover) operator(+=) integer(1)operator(;) + ident(gLastTickCount) operator(=) ident(tick)operator(;) + ident(Int64) ident(now) operator(=) operator((()ident(Int64)operator(\))ident(gTickCountTickover) operator(<<) integer(32)operator(\)) operator(+) operator(()ident(Int64)operator(\))ident(tick)operator(;) + preprocessor(#endif) + + reserved(while) operator(()pre_constant(true)operator(\)) operator({) + ident(multimap)operator(<)ident(Int64)operator(,)ident(Timer_t)operator(>::)ident(iterator) ident(i) operator(=) ident(Timers)operator(.)ident(begin)operator((\);) + reserved(if) operator(()ident(i) operator(==) ident(Timers)operator(.)ident(end)operator((\)\)) + reserved(break)operator(;) + reserved(if) operator(()ident(i)operator(->)ident(first) operator(>) ident(now)operator(\)) + reserved(break)operator(;) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)) operator(()string<delimiter(")delimiter(")>operator(,) ident(EM_TIMER_FIRED)operator(,) ident(i)operator(->)ident(second)operator(.)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(i)operator(->)ident(second)operator(.)ident(GetBinding)operator((\))operator(.)ident(length)operator((\)\);) + ident(Timers)operator(.)ident(erase) operator(()ident(i)operator(\);) + operator(}) + reserved(return) pre_constant(true)operator(;) +operator(}) + + + +comment(/*********************************** +EventMachine_t::InstallOneshotTimer +***********************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(InstallOneshotTimer) operator(()pre_type(int) ident(milliseconds)operator(\)) +operator({) + reserved(if) operator(()ident(Timers)operator(.)ident(size)operator((\)) operator(>) ident(MaxOutstandingTimers)operator(\)) + reserved(return) pre_constant(false)operator(;) + comment(// Don't use the global loop-time variable here, because we might) + comment(// get called before the main event machine is running.) + + preprocessor(#ifdef) ident(OS_UNIX) + reserved(struct) ident(timeval) ident(tv)operator(;) + ident(gettimeofday) operator((&)ident(tv)operator(,) pre_constant(NULL)operator(\);) + ident(Int64) ident(fire_at) operator(=) operator(((()ident(Int64)operator(\)()ident(tv)operator(.)ident(tv_sec)operator(\)\)) operator(*) integer(1000000LL)operator(\)) operator(+) operator((()ident(Int64)operator(\)()ident(tv)operator(.)ident(tv_usec)operator(\)\);) + ident(fire_at) operator(+=) operator((()ident(Int64)operator(\))ident(milliseconds)operator(\)) operator(*) integer(1000LL)operator(;) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + pre_type(unsigned) ident(tick) operator(=) ident(GetTickCount)operator((\);) + reserved(if) operator(()ident(tick) operator(<) ident(gLastTickCount)operator(\)) + ident(gTickCountTickover) operator(+=) integer(1)operator(;) + ident(gLastTickCount) operator(=) ident(tick)operator(;) + + ident(Int64) ident(fire_at) operator(=) operator((()ident(Int64)operator(\))ident(gTickCountTickover) operator(<<) integer(32)operator(\)) operator(+) operator(()ident(Int64)operator(\))ident(tick)operator(;) + ident(fire_at) operator(+=) operator(()ident(Int64)operator(\))ident(milliseconds)operator(;) + preprocessor(#endif) + + ident(Timer_t) ident(t)operator(;) + ident(multimap)operator(<)ident(Int64)operator(,)ident(Timer_t)operator(>::)ident(iterator) ident(i) operator(=) + ident(Timers)operator(.)ident(insert) operator(()ident(make_pair) operator(()ident(fire_at)operator(,) ident(t)operator(\)\);) + reserved(return) ident(i)operator(->)ident(second)operator(.)ident(GetBindingChars)operator((\);) +operator(}) + + +comment(/******************************* +EventMachine_t::ConnectToServer +*******************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(ConnectToServer) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + comment(/* We want to spend no more than a few seconds waiting for a connection + * to a remote host. So we use a nonblocking connect. + * Linux disobeys the usual rules for nonblocking connects. + * Per Stevens (UNP p.410\), you expect a nonblocking connect to select + * both readable and writable on error, and not to return EINPROGRESS + * if the connect can be fulfilled immediately. Linux violates both + * of these expectations. + * Any kind of nonblocking connect on Linux returns EINPROGRESS. + * The socket will then return writable when the disposition of the + * connect is known, but it will not also be readable in case of + * error! Weirdly, it will be readable in case there is data to read!!! + * (Which can happen with protocols like SSH and SMTP.\) + * I suppose if you were so inclined you could consider this logical, + * but it's not the way Unix has historically done it. + * So we ignore the readable flag and read getsockopt to see if there + * was an error connecting. A select timeout works as expected. + * In regard to getsockopt: Linux does the Berkeley-style thing, + * not the Solaris-style, and returns zero with the error code in + * the error parameter. + * Return the binding-text of the newly-created pending connection, + * or NULL if there was a problem. + */) + + reserved(if) operator((!)ident(server) operator(||) operator(!*)ident(server) operator(||) operator(!)ident(port)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + pre_type(int) ident(family)operator(,) ident(bind_size)operator(;) + reserved(struct) ident(sockaddr) operator(*)ident(bind_as) operator(=) ident(name2address) operator(()ident(server)operator(,) ident(port)operator(,) operator(&)ident(family)operator(,) operator(&)ident(bind_size)operator(\);) + reserved(if) operator((!)ident(bind_as)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + pre_type(int) ident(sd) operator(=) ident(socket) operator(()ident(family)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + comment(/* + sockaddr_in pin; + unsigned long HostAddr; + + HostAddr = inet_addr (server\); + if (HostAddr == INADDR_NONE\) { + hostent *hp = gethostbyname ((char*\)server\); // Windows requires (char*\) + if (!hp\) { + // TODO: This gives the caller a fatal error. Not good. + // They can respond by catching RuntimeError (blecch\). + // Possibly we need to fire an unbind event and provide + // a status code so user code can detect the cause of the + // failure. + return NULL; + } + HostAddr = ((in_addr*\)(hp->h_addr\)\)->s_addr; + } + + memset (&pin, 0, sizeof(pin\)\); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = HostAddr; + pin.sin_port = htons (port\); + + int sd = socket (AF_INET, SOCK_STREAM, 0\); + if (sd == INVALID_SOCKET\) + return NULL; + */) + + comment(// From here on, ALL error returns must close the socket.) + comment(// Set the new socket nonblocking.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(sd)operator(\)\)) operator({) + ident(closesocket) operator(()ident(sd)operator(\);) + reserved(return) pre_constant(NULL)operator(;) + operator(}) + comment(// Disable slow-start (Nagle algorithm\).) + pre_type(int) ident(one) operator(=) integer(1)operator(;) + ident(setsockopt) operator(()ident(sd)operator(,) ident(IPPROTO_TCP)operator(,) ident(TCP_NODELAY)operator(,) operator(()pre_type(char)operator(*\)) operator(&)ident(one)operator(,) reserved(sizeof)operator(()ident(one)operator(\)\);) + + directive(const) pre_type(char) operator(*)ident(out) operator(=) pre_constant(NULL)operator(;) + + preprocessor(#ifdef) ident(OS_UNIX) + comment(//if (connect (sd, (sockaddr*\)&pin, sizeof pin\) == 0\) {) + reserved(if) operator(()ident(connect) operator(()ident(sd)operator(,) ident(bind_as)operator(,) ident(bind_size)operator(\)) operator(==) integer(0)operator(\)) operator({) + comment(// This is a connect success, which Linux appears) + comment(// never to give when the socket is nonblocking,) + comment(// even if the connection is intramachine or to) + comment(// localhost.) + + comment(/* Changed this branch 08Aug06. Evidently some kernels + * (FreeBSD for example\) will actually return success from + * a nonblocking connect. This is a pretty simple case, + * just set up the new connection and clear the pending flag. + * Thanks to Chris Ochs for helping track this down. + * This branch never gets taken on Linux or (oddly\) OSX. + * The original behavior was to throw an unimplemented, + * which the user saw as a fatal exception. Very unfriendly. + * + * Tweaked 10Aug06. Even though the connect disposition is + * known, we still set the connect-pending flag. That way + * some needed initialization will happen in the ConnectionDescriptor. + * (To wit, the ConnectionCompleted event gets sent to the client.\) + */) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(sd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no connection allocated)delimiter(")>operator(\);) + ident(cd)operator(->)ident(SetConnectPending) operator(()pre_constant(true)operator(\);) + ident(Add) operator(()ident(cd)operator(\);) + ident(out) operator(=) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + reserved(else) reserved(if) operator(()ident(errno) operator(==) ident(EINPROGRESS)operator(\)) operator({) + comment(// Errno will generally always be EINPROGRESS, but on Linux) + comment(// we have to look at getsockopt to be sure what really happened.) + pre_type(int) ident(error)operator(;) + ident(socklen_t) ident(len)operator(;) + ident(len) operator(=) reserved(sizeof)operator(()ident(error)operator(\);) + pre_type(int) ident(o) operator(=) ident(getsockopt) operator(()ident(sd)operator(,) ident(SOL_SOCKET)operator(,) ident(SO_ERROR)operator(,) operator(&)ident(error)operator(,) operator(&)ident(len)operator(\);) + reserved(if) operator((()ident(o) operator(==) integer(0)operator(\)) operator(&&) operator(()ident(error) operator(==) integer(0)operator(\)\)) operator({) + comment(// Here, there's no disposition.) + comment(// Put the connection on the stack and wait for it to complete) + comment(// or time out.) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(sd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no connection allocated)delimiter(")>operator(\);) + ident(cd)operator(->)ident(SetConnectPending) operator(()pre_constant(true)operator(\);) + ident(Add) operator(()ident(cd)operator(\);) + ident(out) operator(=) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + reserved(else) operator({) + comment(/* This could be connection refused or some such thing. + * We will come here on Linux if a localhost connection fails. + * Changed 16Jul06: Originally this branch was a no-op, and + * we'd drop down to the end of the method, close the socket, + * and return NULL, which would cause the caller to GET A + * FATAL EXCEPTION. Now we keep the socket around but schedule an + * immediate close on it, so the caller will get a close-event + * scheduled on it. This was only an issue for localhost connections + * to non-listening ports. We may eventually need to revise this + * revised behavior, in case it causes problems like making it hard + * for people to know that a failure occurred. + */) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(sd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no connection allocated)delimiter(")>operator(\);) + ident(cd)operator(->)ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + ident(Add) operator(()ident(cd)operator(\);) + ident(out) operator(=) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + operator(}) + reserved(else) operator({) + comment(// The error from connect was something other then EINPROGRESS.) + operator(}) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + comment(//if (connect (sd, (sockaddr*\)&pin, sizeof pin\) == 0\) {) + reserved(if) operator(()ident(connect) operator(()ident(sd)operator(,) ident(bind_as)operator(,) ident(bind_size)operator(\)) operator(==) integer(0)operator(\)) operator({) + comment(// This is a connect success, which Windows appears) + comment(// never to give when the socket is nonblocking,) + comment(// even if the connection is intramachine or to) + comment(// localhost.) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unimplemented)delimiter(")>operator(\);) + operator(}) + reserved(else) reserved(if) operator(()ident(WSAGetLastError)operator((\)) operator(==) ident(WSAEWOULDBLOCK)operator(\)) operator({) + comment(// Here, there's no disposition.) + comment(// Windows appears not to surface refused connections or) + comment(// such stuff at this point.) + comment(// Put the connection on the stack and wait for it to complete) + comment(// or time out.) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(sd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no connection allocated)delimiter(")>operator(\);) + ident(cd)operator(->)ident(SetConnectPending) operator(()pre_constant(true)operator(\);) + ident(Add) operator(()ident(cd)operator(\);) + ident(out) operator(=) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + reserved(else) operator({) + comment(// The error from connect was something other then WSAEWOULDBLOCK.) + operator(}) + + preprocessor(#endif) + + reserved(if) operator(()ident(out) operator(==) pre_constant(NULL)operator(\)) + ident(closesocket) operator(()ident(sd)operator(\);) + reserved(return) ident(out)operator(;) +operator(}) + +comment(/*********************************** +EventMachine_t::ConnectToUnixServer +***********************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(ConnectToUnixServer) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(\)) +operator({) + comment(/* Connect to a Unix-domain server, which by definition is running + * on the same host. + * There is no meaningful implementation on Windows. + * There's no need to do a nonblocking connect, since the connection + * is always local and can always be fulfilled immediately. + */) + + preprocessor(#ifdef) ident(OS_WIN32) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unix-domain connection unavailable on this platform)delimiter(")>operator(\);) + reserved(return) pre_constant(NULL)operator(;) + preprocessor(#endif) + + comment(// The whole rest of this function is only compiled on Unix systems.) + preprocessor(#ifdef) ident(OS_UNIX) + + directive(const) pre_type(char) operator(*)ident(out) operator(=) pre_constant(NULL)operator(;) + + reserved(if) operator((!)ident(server) operator(||) operator(!*)ident(server)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + ident(sockaddr_un) ident(pun)operator(;) + ident(memset) operator((&)ident(pun)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(pun)operator(\)\);) + ident(pun)operator(.)ident(sun_family) operator(=) ident(AF_LOCAL)operator(;) + + comment(// You ordinarily expect the server name field to be at least 1024 bytes long,) + comment(// but on Linux it can be MUCH shorter.) + reserved(if) operator(()ident(strlen)operator(()ident(server)operator(\)) operator(>=) reserved(sizeof)operator(()ident(pun)operator(.)ident(sun_path)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unix-domain server name is too long)delimiter(")>operator(\);) + + + ident(strcpy) operator(()ident(pun)operator(.)ident(sun_path)operator(,) ident(server)operator(\);) + + pre_type(int) ident(fd) operator(=) ident(socket) operator(()ident(AF_LOCAL)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(fd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + comment(// From here on, ALL error returns must close the socket.) + comment(// NOTE: At this point, the socket is still a blocking socket.) + reserved(if) operator(()ident(connect) operator(()ident(fd)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(pun)operator(,) reserved(sizeof)operator(()ident(pun)operator(\)\)) operator(!=) integer(0)operator(\)) operator({) + ident(closesocket) operator(()ident(fd)operator(\);) + reserved(return) pre_constant(NULL)operator(;) + operator(}) + + comment(// Set the newly-connected socket nonblocking.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(fd)operator(\)\)) operator({) + ident(closesocket) operator(()ident(fd)operator(\);) + reserved(return) pre_constant(NULL)operator(;) + operator(}) + + comment(// Set up a connection descriptor and add it to the event-machine.) + comment(// Observe, even though we know the connection status is connect-success,) + comment(// we still set the "pending" flag, so some needed initializations take) + comment(// place.) + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(fd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no connection allocated)delimiter(")>operator(\);) + ident(cd)operator(->)ident(SetConnectPending) operator(()pre_constant(true)operator(\);) + ident(Add) operator(()ident(cd)operator(\);) + ident(out) operator(=) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + + reserved(if) operator(()ident(out) operator(==) pre_constant(NULL)operator(\)) + ident(closesocket) operator(()ident(fd)operator(\);) + + reserved(return) ident(out)operator(;) + preprocessor(#endif) +operator(}) + +comment(/************************ +EventMachine_t::AttachFD +************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(AttachFD) operator(()pre_type(int) ident(fd)operator(,) pre_type(bool) ident(notify_readable)operator(,) pre_type(bool) ident(notify_writable)operator(\)) +operator({) + preprocessor(#ifdef) ident(OS_UNIX) + reserved(if) operator(()ident(fcntl)operator(()ident(fd)operator(,) ident(F_GETFL)operator(,) integer(0)operator(\)) operator(<) integer(0)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(invalid file descriptor)delimiter(")>operator(\);) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + comment(// TODO: add better check for invalid file descriptors (see ioctlsocket or getsockopt\)) + reserved(if) operator(()ident(fd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(invalid file descriptor)delimiter(")>operator(\);) + preprocessor(#endif) + + operator({//) ident(Check) reserved(for) ident(duplicate) ident(descriptors) + ident(size_t) ident(i)operator(;) + reserved(for) operator(()ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) ident(Descriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + reserved(if) operator(()ident(ed)operator(->)ident(GetSocket)operator((\)) operator(==) ident(fd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(adding existing descriptor)delimiter(")>operator(\);) + operator(}) + + reserved(for) operator(()ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) ident(NewDescriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(NewDescriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ed)operator(\);) + reserved(if) operator(()ident(ed)operator(->)ident(GetSocket)operator((\)) operator(==) ident(fd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(adding existing new descriptor)delimiter(")>operator(\);) + operator(}) + operator(}) + + ident(ConnectionDescriptor) operator(*)ident(cd) operator(=) reserved(new) ident(ConnectionDescriptor) operator(()ident(fd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(cd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no connection allocated)delimiter(")>operator(\);) + + ident(cd)operator(->)ident(SetConnectPending) operator(()pre_constant(true)operator(\);) + ident(cd)operator(->)ident(SetNotifyReadable) operator(()ident(notify_readable)operator(\);) + ident(cd)operator(->)ident(SetNotifyWritable) operator(()ident(notify_writable)operator(\);) + + ident(Add) operator(()ident(cd)operator(\);) + + directive(const) pre_type(char) operator(*)ident(out) operator(=) pre_constant(NULL)operator(;) + ident(out) operator(=) ident(cd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + reserved(if) operator(()ident(out) operator(==) pre_constant(NULL)operator(\)) + ident(closesocket) operator(()ident(fd)operator(\);) + reserved(return) ident(out)operator(;) +operator(}) + +comment(/************************ +EventMachine_t::DetachFD +************************/) + +pre_type(int) ident(EventMachine_t)operator(::)ident(DetachFD) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + reserved(if) operator((!)ident(ed)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(detaching bad descriptor)delimiter(")>operator(\);) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + reserved(if) operator(()ident(bEpoll)operator(\)) operator({) + reserved(if) operator(()ident(ed)operator(->)ident(GetSocket)operator((\)) operator(!=) ident(INVALID_SOCKET)operator(\)) operator({) + ident(assert) operator(()ident(bEpoll)operator(\);) comment(// wouldn't be in this method otherwise.) + ident(assert) operator(()ident(epfd) operator(!=) operator(-)integer(1)operator(\);) + pre_type(int) ident(e) operator(=) ident(epoll_ctl) operator(()ident(epfd)operator(,) ident(EPOLL_CTL_DEL)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(ed)operator(->)ident(GetEpollEvent)operator((\)\);) + comment(// ENOENT or EBADF are not errors because the socket may be already closed when we get here.) + reserved(if) operator(()ident(e) operator(&&) operator(()ident(errno) operator(!=) ident(ENOENT)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(EBADF)operator(\)\)) operator({) + pre_type(char) ident(buf) operator([)integer(200)operator(];) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(unable to delete epoll event: %s)delimiter(")>operator(,) ident(strerror)operator(()ident(errno)operator(\)\);) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(buf)operator(\);) + operator(}) + operator(}) + operator(}) + preprocessor(#endif) + + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(if) operator(()ident(bKqueue)operator(\)) operator({) + reserved(struct) ident(kevent) ident(k)operator(;) + ident(EV_SET) operator((&)ident(k)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(EVFILT_READ)operator(,) ident(EV_DELETE)operator(,) integer(0)operator(,) integer(0)operator(,) ident(ed)operator(\);) + pre_type(int) ident(t) operator(=) ident(kevent) operator(()ident(kqfd)operator(,) operator(&)ident(k)operator(,) integer(1)operator(,) pre_constant(NULL)operator(,) integer(0)operator(,) pre_constant(NULL)operator(\);) + ident(assert) operator(()ident(t) operator(==) integer(0)operator(\);) + operator(}) + preprocessor(#endif) + + operator({) comment(// remove descriptor from lists) + pre_type(int) ident(i)operator(,) ident(j)operator(;) + pre_type(int) ident(nSockets) operator(=) ident(Descriptors)operator(.)ident(size)operator((\);) + reserved(for) operator(()ident(i)operator(=)integer(0)operator(,) ident(j)operator(=)integer(0)operator(;) ident(i) operator(<) ident(nSockets)operator(;) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ted) operator(=) ident(Descriptors)operator([)ident(i)operator(];) + ident(assert) operator(()ident(ted)operator(\);) + reserved(if) operator(()ident(ted) operator(!=) ident(ed)operator(\)) + ident(Descriptors) operator([)ident(j)operator(++]) operator(=) ident(ted)operator(;) + operator(}) + reserved(while) operator((()ident(size_t)operator(\))ident(j) operator(<) ident(Descriptors)operator(.)ident(size)operator((\)\)) + ident(Descriptors)operator(.)ident(pop_back)operator((\);) + + ident(ModifiedDescriptors)operator(.)ident(erase) operator(()ident(ed)operator(\);) + operator(}) + + pre_type(int) ident(fd) operator(=) ident(ed)operator(->)ident(GetSocket)operator((\);) + + comment(// We depend on ~EventableDescriptor not calling close(\) if the socket is invalid) + ident(ed)operator(->)ident(SetSocketInvalid)operator((\);) + reserved(delete) ident(ed)operator(;) + + reserved(return) ident(fd)operator(;) +operator(}) + +comment(/************ +name2address +************/) + +reserved(struct) ident(sockaddr) operator(*)ident(name2address) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(,) pre_type(int) operator(*)ident(family)operator(,) pre_type(int) operator(*)ident(bind_size)operator(\)) +operator({) + comment(// THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed.) + comment(// Check the more-common cases first.) + comment(// Return NULL if no resolution.) + + directive(static) reserved(struct) ident(sockaddr_in) ident(in4)operator(;) + preprocessor(#ifndef) ident(__CYGWIN__) + directive(static) reserved(struct) ident(sockaddr_in6) ident(in6)operator(;) + preprocessor(#endif) + reserved(struct) ident(hostent) operator(*)ident(hp)operator(;) + + reserved(if) operator((!)ident(server) operator(||) operator(!*)ident(server)operator(\)) + ident(server) operator(=) string<delimiter(")content(0.0.0.0)delimiter(")>operator(;) + + ident(memset) operator((&)ident(in4)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(in4)operator(\)\);) + reserved(if) operator(() operator(()ident(in4)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(inet_addr) operator(()ident(server)operator(\)\)) operator(!=) ident(INADDR_NONE)operator(\)) operator({) + reserved(if) operator(()ident(family)operator(\)) + operator(*)ident(family) operator(=) ident(AF_INET)operator(;) + reserved(if) operator(()ident(bind_size)operator(\)) + operator(*)ident(bind_size) operator(=) reserved(sizeof)operator(()ident(in4)operator(\);) + ident(in4)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(in4)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + reserved(return) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(in4)operator(;) + operator(}) + + preprocessor(#if) ident(defined)operator(()ident(OS_UNIX)operator(\)) operator(&&) operator(!)ident(defined)operator(()ident(__CYGWIN__)operator(\)) + ident(memset) operator((&)ident(in6)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(in6)operator(\)\);) + reserved(if) operator(()ident(inet_pton) operator(()ident(AF_INET6)operator(,) ident(server)operator(,) ident(in6)operator(.)ident(sin6_addr)operator(.)ident(s6_addr)operator(\)) operator(>) integer(0)operator(\)) operator({) + reserved(if) operator(()ident(family)operator(\)) + operator(*)ident(family) operator(=) ident(AF_INET6)operator(;) + reserved(if) operator(()ident(bind_size)operator(\)) + operator(*)ident(bind_size) operator(=) reserved(sizeof)operator(()ident(in6)operator(\);) + ident(in6)operator(.)ident(sin6_family) operator(=) ident(AF_INET6)operator(;) + ident(in6)operator(.)ident(sin6_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + reserved(return) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(in6)operator(;) + operator(}) + preprocessor(#endif) + + preprocessor(#ifdef) ident(OS_WIN32) + comment(// TODO, must complete this branch. Windows doesn't have inet_pton.) + comment(// A possible approach is to make a getaddrinfo call with the supplied) + comment(// server address, constraining the hints to ipv6 and seeing if we) + comment(// get any addresses.) + comment(// For the time being, Ipv6 addresses aren't supported on Windows.) + preprocessor(#endif) + + ident(hp) operator(=) ident(gethostbyname) operator((()pre_type(char)operator(*\))ident(server)operator(\);) comment(// Windows requires the cast.) + reserved(if) operator(()ident(hp)operator(\)) operator({) + ident(in4)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) operator((()ident(in_addr)operator(*\)()ident(hp)operator(->)ident(h_addr)operator(\)\)->)ident(s_addr)operator(;) + reserved(if) operator(()ident(family)operator(\)) + operator(*)ident(family) operator(=) ident(AF_INET)operator(;) + reserved(if) operator(()ident(bind_size)operator(\)) + operator(*)ident(bind_size) operator(=) reserved(sizeof)operator(()ident(in4)operator(\);) + ident(in4)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(in4)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + reserved(return) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(in4)operator(;) + operator(}) + + reserved(return) pre_constant(NULL)operator(;) +operator(}) + + +comment(/******************************* +EventMachine_t::CreateTcpServer +*******************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(CreateTcpServer) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + comment(/* Create a TCP-acceptor (server\) socket and add it to the event machine. + * Return the binding of the new acceptor to the caller. + * This binding will be referenced when the new acceptor sends events + * to indicate accepted connections. + */) + + + pre_type(int) ident(family)operator(,) ident(bind_size)operator(;) + reserved(struct) ident(sockaddr) operator(*)ident(bind_here) operator(=) ident(name2address) operator(()ident(server)operator(,) ident(port)operator(,) operator(&)ident(family)operator(,) operator(&)ident(bind_size)operator(\);) + reserved(if) operator((!)ident(bind_here)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + directive(const) pre_type(char) operator(*)ident(output_binding) operator(=) pre_constant(NULL)operator(;) + + comment(//struct sockaddr_in sin;) + + pre_type(int) ident(sd_accept) operator(=) ident(socket) operator(()ident(family)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd_accept) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + reserved(goto) ident(fail)operator(;) + operator(}) + + comment(/* + memset (&sin, 0, sizeof(sin\)\); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons (port\); + + if (server && *server\) { + sin.sin_addr.s_addr = inet_addr (server\); + if (sin.sin_addr.s_addr == INADDR_NONE\) { + hostent *hp = gethostbyname ((char*\)server\); // Windows requires the cast. + if (hp == NULL\) { + //__warning ("hostname not resolved: ", server\); + goto fail; + } + sin.sin_addr.s_addr = ((in_addr*\)(hp->h_addr\)\)->s_addr; + } + } + */) + + operator({) comment(// set reuseaddr to improve performance on restarts.) + pre_type(int) ident(oval) operator(=) integer(1)operator(;) + reserved(if) operator(()ident(setsockopt) operator(()ident(sd_accept)operator(,) ident(SOL_SOCKET)operator(,) ident(SO_REUSEADDR)operator(,) operator(()pre_type(char)operator(*\)&)ident(oval)operator(,) reserved(sizeof)operator(()ident(oval)operator(\)\)) operator(<) integer(0)operator(\)) operator({) + comment(//__warning ("setsockopt failed while creating listener",""\);) + reserved(goto) ident(fail)operator(;) + operator(}) + operator(}) + + operator({) comment(// set CLOEXEC. Only makes sense on Unix) + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(cloexec) operator(=) ident(fcntl) operator(()ident(sd_accept)operator(,) ident(F_GETFD)operator(,) integer(0)operator(\);) + ident(assert) operator(()ident(cloexec) operator(>=) integer(0)operator(\);) + ident(cloexec) operator(|=) ident(FD_CLOEXEC)operator(;) + ident(fcntl) operator(()ident(sd_accept)operator(,) ident(F_SETFD)operator(,) ident(cloexec)operator(\);) + preprocessor(#endif) + operator(}) + + + comment(//if (bind (sd_accept, (struct sockaddr*\)&sin, sizeof(sin\)\)\) {) + reserved(if) operator(()ident(bind) operator(()ident(sd_accept)operator(,) ident(bind_here)operator(,) ident(bind_size)operator(\)\)) operator({) + comment(//__warning ("binding failed"\);) + reserved(goto) ident(fail)operator(;) + operator(}) + + reserved(if) operator(()ident(listen) operator(()ident(sd_accept)operator(,) integer(100)operator(\)\)) operator({) + comment(//__warning ("listen failed"\);) + reserved(goto) ident(fail)operator(;) + operator(}) + + operator({) + comment(// Set the acceptor non-blocking.) + comment(// THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(sd_accept)operator(\)\)) operator({) + comment(//int val = fcntl (sd_accept, F_GETFL, 0\);) + comment(//if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK\) == -1\) {) + reserved(goto) ident(fail)operator(;) + operator(}) + operator(}) + + operator({) comment(// Looking good.) + ident(AcceptorDescriptor) operator(*)ident(ad) operator(=) reserved(new) ident(AcceptorDescriptor) operator(()ident(sd_accept)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(ad)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to allocate acceptor)delimiter(")>operator(\);) + ident(Add) operator(()ident(ad)operator(\);) + ident(output_binding) operator(=) ident(ad)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + + reserved(return) ident(output_binding)operator(;) + + label(fail)operator(:) + reserved(if) operator(()ident(sd_accept) operator(!=) ident(INVALID_SOCKET)operator(\)) + ident(closesocket) operator(()ident(sd_accept)operator(\);) + reserved(return) pre_constant(NULL)operator(;) +operator(}) + + +comment(/********************************** +EventMachine_t::OpenDatagramSocket +**********************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(OpenDatagramSocket) operator(()directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(output_binding) operator(=) pre_constant(NULL)operator(;) + + pre_type(int) ident(sd) operator(=) ident(socket) operator(()ident(AF_INET)operator(,) ident(SOCK_DGRAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(goto) ident(fail)operator(;) + comment(// from here on, early returns must close the socket!) + + + reserved(struct) ident(sockaddr_in) ident(sin)operator(;) + ident(memset) operator((&)ident(sin)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(sin)operator(\)\);) + ident(sin)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(sin)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + + + reserved(if) operator(()ident(address) operator(&&) operator(*)ident(address)operator(\)) operator({) + ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(inet_addr) operator(()ident(address)operator(\);) + reserved(if) operator(()ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(==) ident(INADDR_NONE)operator(\)) operator({) + ident(hostent) operator(*)ident(hp) operator(=) ident(gethostbyname) operator((()pre_type(char)operator(*\))ident(address)operator(\);) comment(// Windows requires the cast.) + reserved(if) operator(()ident(hp) operator(==) pre_constant(NULL)operator(\)) + reserved(goto) ident(fail)operator(;) + ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) operator((()ident(in_addr)operator(*\)()ident(hp)operator(->)ident(h_addr)operator(\)\)->)ident(s_addr)operator(;) + operator(}) + operator(}) + reserved(else) + ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(htonl) operator(()ident(INADDR_ANY)operator(\);) + + + comment(// Set the new socket nonblocking.) + operator({) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(sd)operator(\)\)) + comment(//int val = fcntl (sd, F_GETFL, 0\);) + comment(//if (fcntl (sd, F_SETFL, val | O_NONBLOCK\) == -1\)) + reserved(goto) ident(fail)operator(;) + operator(}) + + reserved(if) operator(()ident(bind) operator(()ident(sd)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(sin)operator(,) reserved(sizeof)operator(()ident(sin)operator(\)\)) operator(!=) integer(0)operator(\)) + reserved(goto) ident(fail)operator(;) + + operator({) comment(// Looking good.) + ident(DatagramDescriptor) operator(*)ident(ds) operator(=) reserved(new) ident(DatagramDescriptor) operator(()ident(sd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(ds)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to allocate datagram-socket)delimiter(")>operator(\);) + ident(Add) operator(()ident(ds)operator(\);) + ident(output_binding) operator(=) ident(ds)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + + reserved(return) ident(output_binding)operator(;) + + label(fail)operator(:) + reserved(if) operator(()ident(sd) operator(!=) ident(INVALID_SOCKET)operator(\)) + ident(closesocket) operator(()ident(sd)operator(\);) + reserved(return) pre_constant(NULL)operator(;) +operator(}) + + + +comment(/******************* +EventMachine_t::Add +*******************/) + +directive(void) ident(EventMachine_t)operator(::)ident(Add) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + reserved(if) operator((!)ident(ed)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(added bad descriptor)delimiter(")>operator(\);) + ident(ed)operator(->)ident(SetEventCallback) operator(()ident(EventCallback)operator(\);) + ident(NewDescriptors)operator(.)ident(push_back) operator(()ident(ed)operator(\);) +operator(}) + + +comment(/******************************* +EventMachine_t::ArmKqueueWriter +*******************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(ArmKqueueWriter) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(if) operator(()ident(bKqueue)operator(\)) operator({) + reserved(if) operator((!)ident(ed)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(added bad descriptor)delimiter(")>operator(\);) + reserved(struct) ident(kevent) ident(k)operator(;) + ident(EV_SET) operator((&)ident(k)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(EVFILT_WRITE)operator(,) ident(EV_ADD) operator(|) ident(EV_ONESHOT)operator(,) integer(0)operator(,) integer(0)operator(,) ident(ed)operator(\);) + pre_type(int) ident(t) operator(=) ident(kevent) operator(()ident(kqfd)operator(,) operator(&)ident(k)operator(,) integer(1)operator(,) pre_constant(NULL)operator(,) integer(0)operator(,) pre_constant(NULL)operator(\);) + ident(assert) operator(()ident(t) operator(==) integer(0)operator(\);) + operator(}) + preprocessor(#endif) +operator(}) + +comment(/******************************* +EventMachine_t::ArmKqueueReader +*******************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(ArmKqueueReader) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(if) operator(()ident(bKqueue)operator(\)) operator({) + reserved(if) operator((!)ident(ed)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(added bad descriptor)delimiter(")>operator(\);) + reserved(struct) ident(kevent) ident(k)operator(;) + ident(EV_SET) operator((&)ident(k)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(EVFILT_READ)operator(,) ident(EV_ADD)operator(,) integer(0)operator(,) integer(0)operator(,) ident(ed)operator(\);) + pre_type(int) ident(t) operator(=) ident(kevent) operator(()ident(kqfd)operator(,) operator(&)ident(k)operator(,) integer(1)operator(,) pre_constant(NULL)operator(,) integer(0)operator(,) pre_constant(NULL)operator(\);) + ident(assert) operator(()ident(t) operator(==) integer(0)operator(\);) + operator(}) + preprocessor(#endif) +operator(}) + +comment(/********************************** +EventMachine_t::_AddNewDescriptors +**********************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_AddNewDescriptors)operator((\)) +operator({) + comment(/* Avoid adding descriptors to the main descriptor list + * while we're actually traversing the list. + * Any descriptors that are added as a result of processing timers + * or acceptors should go on a temporary queue and then added + * while we're not traversing the main list. + * Also, it (rarely\) happens that a newly-created descriptor + * is immediately scheduled to close. It might be a good + * idea not to bother scheduling these for I/O but if + * we do that, we might bypass some important processing. + */) + + reserved(for) operator(()ident(size_t) ident(i) operator(=) integer(0)operator(;) ident(i) operator(<) ident(NewDescriptors)operator(.)ident(size)operator((\);) ident(i)operator(++\)) operator({) + ident(EventableDescriptor) operator(*)ident(ed) operator(=) ident(NewDescriptors)operator([)ident(i)operator(];) + reserved(if) operator(()ident(ed) operator(==) pre_constant(NULL)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(adding bad descriptor)delimiter(")>operator(\);) + + preprocessor(#if) ident(HAVE_EPOLL) + reserved(if) operator(()ident(bEpoll)operator(\)) operator({) + ident(assert) operator(()ident(epfd) operator(!=) operator(-)integer(1)operator(\);) + pre_type(int) ident(e) operator(=) ident(epoll_ctl) operator(()ident(epfd)operator(,) ident(EPOLL_CTL_ADD)operator(,) ident(ed)operator(->)ident(GetSocket)operator((\),) ident(ed)operator(->)ident(GetEpollEvent)operator((\)\);) + reserved(if) operator(()ident(e)operator(\)) operator({) + pre_type(char) ident(buf) operator([)integer(200)operator(];) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(unable to add new descriptor: %s)delimiter(")>operator(,) ident(strerror)operator(()ident(errno)operator(\)\);) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()ident(buf)operator(\);) + operator(}) + operator(}) + preprocessor(#endif) + + preprocessor(#if) ident(HAVE_KQUEUE) + comment(/* + if (bKqueue\) { + // INCOMPLETE. Some descriptors don't want to be readable. + assert (kqfd != -1\); + struct kevent k; + EV_SET (&k, ed->GetSocket(\), EVFILT_READ, EV_ADD, 0, 0, ed\); + int t = kevent (kqfd, &k, 1, NULL, 0, NULL\); + assert (t == 0\); + } + */) + preprocessor(#endif) + + ident(Descriptors)operator(.)ident(push_back) operator(()ident(ed)operator(\);) + operator(}) + ident(NewDescriptors)operator(.)ident(clear)operator((\);) +operator(}) + + +comment(/********************************** +EventMachine_t::_ModifyDescriptors +**********************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(_ModifyDescriptors)operator((\)) +operator({) + comment(/* For implementations which don't level check every descriptor on + * every pass through the machine, as select does. + * If we're not selecting, then descriptors need a way to signal to the + * machine that their readable or writable status has changed. + * That's what the ::Modify call is for. We do it this way to avoid + * modifying descriptors during the loop traversal, where it can easily + * happen that an object (like a UDP socket\) gets data written on it by + * the application during #post_init. That would take place BEFORE the + * descriptor even gets added to the epoll descriptor, so the modify + * operation will crash messily. + * Another really messy possibility is for a descriptor to put itself + * on the Modified list, and then get deleted before we get here. + * Remember, deletes happen after the I/O traversal and before the + * next pass through here. So we have to make sure when we delete a + * descriptor to remove it from the Modified list. + */) + + preprocessor(#ifdef) ident(HAVE_EPOLL) + reserved(if) operator(()ident(bEpoll)operator(\)) operator({) + ident(set)operator(<)ident(EventableDescriptor)operator(*>::)ident(iterator) ident(i) operator(=) ident(ModifiedDescriptors)operator(.)ident(begin)operator((\);) + reserved(while) operator(()ident(i) operator(!=) ident(ModifiedDescriptors)operator(.)ident(end)operator((\)\)) operator({) + ident(assert) operator((*)ident(i)operator(\);) + ident(_ModifyEpollEvent) operator((*)ident(i)operator(\);) + operator(++)ident(i)operator(;) + operator(}) + operator(}) + preprocessor(#endif) + + ident(ModifiedDescriptors)operator(.)ident(clear)operator((\);) +operator(}) + + +comment(/********************** +EventMachine_t::Modify +**********************/) + +directive(void) ident(EventMachine_t)operator(::)ident(Modify) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + reserved(if) operator((!)ident(ed)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(modified bad descriptor)delimiter(")>operator(\);) + ident(ModifiedDescriptors)operator(.)ident(insert) operator(()ident(ed)operator(\);) +operator(}) + + +comment(/*********************************** +EventMachine_t::_OpenFileForWriting +***********************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(_OpenFileForWriting) operator(()directive(const) pre_type(char) operator(*)ident(filename)operator(\)) +operator({) + comment(/* + * Return the binding-text of the newly-opened file, + * or NULL if there was a problem. + */) + + reserved(if) operator((!)ident(filename) operator(||) operator(!*)ident(filename)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + pre_type(int) ident(fd) operator(=) ident(open) operator(()ident(filename)operator(,) ident(O_CREAT)operator(|)ident(O_TRUNC)operator(|)ident(O_WRONLY)operator(|)ident(O_NONBLOCK)operator(,) oct(0644)operator(\);) + + ident(FileStreamDescriptor) operator(*)ident(fsd) operator(=) reserved(new) ident(FileStreamDescriptor) operator(()ident(fd)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(fsd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no file-stream allocated)delimiter(")>operator(\);) + ident(Add) operator(()ident(fsd)operator(\);) + reserved(return) ident(fsd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + +operator(}) + + +comment(/************************************** +EventMachine_t::CreateUnixDomainServer +**************************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(CreateUnixDomainServer) operator(()directive(const) pre_type(char) operator(*)ident(filename)operator(\)) +operator({) + comment(/* Create a UNIX-domain acceptor (server\) socket and add it to the event machine. + * Return the binding of the new acceptor to the caller. + * This binding will be referenced when the new acceptor sends events + * to indicate accepted connections. + * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS. + */) + + preprocessor(#ifdef) ident(OS_WIN32) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unix-domain server unavailable on this platform)delimiter(")>operator(\);) + preprocessor(#endif) + + comment(// The whole rest of this function is only compiled on Unix systems.) + preprocessor(#ifdef) ident(OS_UNIX) + directive(const) pre_type(char) operator(*)ident(output_binding) operator(=) pre_constant(NULL)operator(;) + + reserved(struct) ident(sockaddr_un) ident(s_sun)operator(;) + + pre_type(int) ident(sd_accept) operator(=) ident(socket) operator(()ident(AF_LOCAL)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd_accept) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + reserved(goto) ident(fail)operator(;) + operator(}) + + reserved(if) operator((!)ident(filename) operator(||) operator(!*)ident(filename)operator(\)) + reserved(goto) ident(fail)operator(;) + ident(unlink) operator(()ident(filename)operator(\);) + + ident(bzero) operator((&)ident(s_sun)operator(,) reserved(sizeof)operator(()ident(s_sun)operator(\)\);) + ident(s_sun)operator(.)ident(sun_family) operator(=) ident(AF_LOCAL)operator(;) + ident(strncpy) operator(()ident(s_sun)operator(.)ident(sun_path)operator(,) ident(filename)operator(,) reserved(sizeof)operator(()ident(s_sun)operator(.)ident(sun_path)operator(\)-)integer(1)operator(\);) + + comment(// don't bother with reuseaddr for a local socket.) + + operator({) comment(// set CLOEXEC. Only makes sense on Unix) + preprocessor(#ifdef) ident(OS_UNIX) + pre_type(int) ident(cloexec) operator(=) ident(fcntl) operator(()ident(sd_accept)operator(,) ident(F_GETFD)operator(,) integer(0)operator(\);) + ident(assert) operator(()ident(cloexec) operator(>=) integer(0)operator(\);) + ident(cloexec) operator(|=) ident(FD_CLOEXEC)operator(;) + ident(fcntl) operator(()ident(sd_accept)operator(,) ident(F_SETFD)operator(,) ident(cloexec)operator(\);) + preprocessor(#endif) + operator(}) + + reserved(if) operator(()ident(bind) operator(()ident(sd_accept)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(s_sun)operator(,) reserved(sizeof)operator(()ident(s_sun)operator(\)\)\)) operator({) + comment(//__warning ("binding failed"\);) + reserved(goto) ident(fail)operator(;) + operator(}) + + reserved(if) operator(()ident(listen) operator(()ident(sd_accept)operator(,) integer(100)operator(\)\)) operator({) + comment(//__warning ("listen failed"\);) + reserved(goto) ident(fail)operator(;) + operator(}) + + operator({) + comment(// Set the acceptor non-blocking.) + comment(// THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(sd_accept)operator(\)\)) operator({) + comment(//int val = fcntl (sd_accept, F_GETFL, 0\);) + comment(//if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK\) == -1\) {) + reserved(goto) ident(fail)operator(;) + operator(}) + operator(}) + + operator({) comment(// Looking good.) + ident(AcceptorDescriptor) operator(*)ident(ad) operator(=) reserved(new) ident(AcceptorDescriptor) operator(()ident(sd_accept)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(ad)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to allocate acceptor)delimiter(")>operator(\);) + ident(Add) operator(()ident(ad)operator(\);) + ident(output_binding) operator(=) ident(ad)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + + reserved(return) ident(output_binding)operator(;) + + label(fail)operator(:) + reserved(if) operator(()ident(sd_accept) operator(!=) ident(INVALID_SOCKET)operator(\)) + ident(closesocket) operator(()ident(sd_accept)operator(\);) + reserved(return) pre_constant(NULL)operator(;) + preprocessor(#endif) comment(// OS_UNIX) +operator(}) + + +comment(/********************* +EventMachine_t::Popen +*********************/) +preprocessor(#if) ident(OBSOLETE) +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(Popen) operator(()directive(const) pre_type(char) operator(*)ident(cmd)operator(,) directive(const) pre_type(char) operator(*)ident(mode)operator(\)) +operator({) + preprocessor(#ifdef) ident(OS_WIN32) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(popen is currently unavailable on this platform)delimiter(")>operator(\);) + preprocessor(#endif) + + comment(// The whole rest of this function is only compiled on Unix systems.) + comment(// Eventually we need this functionality (or a full-duplex equivalent\) on Windows.) + preprocessor(#ifdef) ident(OS_UNIX) + directive(const) pre_type(char) operator(*)ident(output_binding) operator(=) pre_constant(NULL)operator(;) + + ident(FILE) operator(*)ident(fp) operator(=) ident(popen) operator(()ident(cmd)operator(,) ident(mode)operator(\);) + reserved(if) operator((!)ident(fp)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + comment(// From here, all early returns must pclose the stream.) + + comment(// According to the pipe(2\) manpage, descriptors returned from pipe have both) + comment(// CLOEXEC and NONBLOCK clear. Do NOT set CLOEXEC. DO set nonblocking.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(fileno) operator(()ident(fp)operator(\)\)\)) operator({) + ident(pclose) operator(()ident(fp)operator(\);) + reserved(return) pre_constant(NULL)operator(;) + operator(}) + + operator({) comment(// Looking good.) + ident(PipeDescriptor) operator(*)ident(pd) operator(=) reserved(new) ident(PipeDescriptor) operator(()ident(fp)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(pd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to allocate pipe)delimiter(")>operator(\);) + ident(Add) operator(()ident(pd)operator(\);) + ident(output_binding) operator(=) ident(pd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + + reserved(return) ident(output_binding)operator(;) + preprocessor(#endif) +operator(}) +preprocessor(#endif) comment(// OBSOLETE) + +comment(/************************** +EventMachine_t::Socketpair +**************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(Socketpair) operator(()pre_type(char) operator(*) directive(const)operator(*)ident(cmd_strings)operator(\)) +operator({) + preprocessor(#ifdef) ident(OS_WIN32) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(socketpair is currently unavailable on this platform)delimiter(")>operator(\);) + preprocessor(#endif) + + comment(// The whole rest of this function is only compiled on Unix systems.) + comment(// Eventually we need this functionality (or a full-duplex equivalent\) on Windows.) + preprocessor(#ifdef) ident(OS_UNIX) + comment(// Make sure the incoming array of command strings is sane.) + reserved(if) operator((!)ident(cmd_strings)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + pre_type(int) ident(j)operator(;) + reserved(for) operator(()ident(j)operator(=)integer(0)operator(;) ident(j) operator(<) integer(100) operator(&&) ident(cmd_strings)operator([)ident(j)operator(];) ident(j)operator(++\)) + operator(;) + reserved(if) operator((()ident(j)operator(==)integer(0)operator(\)) operator(||) operator(()ident(j)operator(==)integer(100)operator(\)\)) + reserved(return) pre_constant(NULL)operator(;) + + directive(const) pre_type(char) operator(*)ident(output_binding) operator(=) pre_constant(NULL)operator(;) + + pre_type(int) ident(sv)operator([)integer(2)operator(];) + reserved(if) operator(()ident(socketpair) operator(()ident(AF_LOCAL)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(,) ident(sv)operator(\)) operator(<) integer(0)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + comment(// from here, all early returns must close the pair of sockets.) + + comment(// Set the parent side of the socketpair nonblocking.) + comment(// We don't care about the child side, and most child processes will expect their) + comment(// stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out.) + comment(// Obviously DON'T set CLOEXEC.) + reserved(if) operator((!)ident(SetSocketNonblocking) operator(()ident(sv)operator([)integer(0)operator(]\)\)) operator({) + ident(close) operator(()ident(sv)operator([)integer(0)operator(]\);) + ident(close) operator(()ident(sv)operator([)integer(1)operator(]\);) + reserved(return) pre_constant(NULL)operator(;) + operator(}) + + ident(pid_t) ident(f) operator(=) ident(fork)operator((\);) + reserved(if) operator(()ident(f) operator(>) integer(0)operator(\)) operator({) + ident(close) operator(()ident(sv)operator([)integer(1)operator(]\);) + ident(PipeDescriptor) operator(*)ident(pd) operator(=) reserved(new) ident(PipeDescriptor) operator(()ident(sv)operator([)integer(0)operator(],) ident(f)operator(,) local_variable(this)operator(\);) + reserved(if) operator((!)ident(pd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to allocate pipe)delimiter(")>operator(\);) + ident(Add) operator(()ident(pd)operator(\);) + ident(output_binding) operator(=) ident(pd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + operator(}) + reserved(else) reserved(if) operator(()ident(f) operator(==) integer(0)operator(\)) operator({) + ident(close) operator(()ident(sv)operator([)integer(0)operator(]\);) + ident(dup2) operator(()ident(sv)operator([)integer(1)operator(],) ident(STDIN_FILENO)operator(\);) + ident(close) operator(()ident(sv)operator([)integer(1)operator(]\);) + ident(dup2) operator(()ident(STDIN_FILENO)operator(,) ident(STDOUT_FILENO)operator(\);) + ident(execvp) operator(()ident(cmd_strings)operator([)integer(0)operator(],) ident(cmd_strings)operator(+)integer(1)operator(\);) + ident(exit) operator((-)integer(1)operator(\);) comment(// end the child process if the exec doesn't work.) + operator(}) + reserved(else) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no fork)delimiter(")>operator(\);) + + reserved(return) ident(output_binding)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/**************************** +EventMachine_t::OpenKeyboard +****************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(OpenKeyboard)operator((\)) +operator({) + ident(KeyboardDescriptor) operator(*)ident(kd) operator(=) reserved(new) ident(KeyboardDescriptor) operator(()local_variable(this)operator(\);) + reserved(if) operator((!)ident(kd)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no keyboard-object allocated)delimiter(")>operator(\);) + ident(Add) operator(()ident(kd)operator(\);) + reserved(return) ident(kd)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) +operator(}) + + + + + +comment(//#endif // OS_UNIX) + +comment(/***************************************************************************** + +$Id$ + +File: emwin.cpp +Date: 05May06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + +comment(// THIS ENTIRE FILE IS FOR WINDOWS BUILDS ONLY) +comment(// INCOMPLETE AND DISABLED FOR NOW.) +preprocessor(#ifdef) ident(xOS_WIN32) + +preprocessor(#include) include("project.h") + + +comment(// Keep a global variable floating around) +comment(// with the current loop time as set by the Event Machine.) +comment(// This avoids the need for frequent expensive calls to time(NULL\);) +ident(time_t) ident(gCurrentLoopTime)operator(;) + + +comment(/****************************** +EventMachine_t::EventMachine_t +******************************/) + +ident(EventMachine_t)operator(::)ident(EventMachine_t) operator(()directive(void) operator((*)ident(event_callback)operator(\)()directive(const) pre_type(char)operator(*,) pre_type(int)operator(,) directive(const) pre_type(char)operator(*,) pre_type(int)operator(\)\):) + ident(EventCallback) operator(()ident(event_callback)operator(\),) + ident(NextHeartbeatTime) operator(()integer(0)operator(\)) +operator({) + ident(gTerminateSignalReceived) operator(=) pre_constant(false)operator(;) + ident(Iocp) operator(=) pre_constant(NULL)operator(;) +operator(}) + + +comment(/******************************* +EventMachine_t::~EventMachine_t +*******************************/) + +ident(EventMachine_t)operator(::~)ident(EventMachine_t)operator((\)) +operator({) + ident(cerr) operator(<<) string<delimiter(")content(EM __dt)char(\\n)delimiter(")>operator(;) + reserved(if) operator(()ident(Iocp)operator(\)) + ident(CloseHandle) operator(()ident(Iocp)operator(\);) +operator(}) + + +comment(/**************************** +EventMachine_t::ScheduleHalt +****************************/) + +directive(void) ident(EventMachine_t)operator(::)ident(ScheduleHalt)operator((\)) +operator({) + comment(/* This is how we stop the machine. + * This can be called by clients. Signal handlers will probably + * set the global flag. + * For now this means there can only be one EventMachine ever running at a time. + */) + ident(gTerminateSignalReceived) operator(=) pre_constant(true)operator(;) +operator(}) + + + +comment(/******************* +EventMachine_t::Run +*******************/) + +directive(void) ident(EventMachine_t)operator(::)ident(Run)operator((\)) +operator({) + ident(HookControlC) operator(()pre_constant(true)operator(\);) + + ident(Iocp) operator(=) ident(CreateIoCompletionPort) operator(()ident(INVALID_HANDLE_VALUE)operator(,) pre_constant(NULL)operator(,) integer(0)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(Iocp) operator(==) pre_constant(NULL)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no completion port)delimiter(")>operator(\);) + + + ident(DWORD) ident(nBytes)operator(,) ident(nCompletionKey)operator(;) + ident(LPOVERLAPPED) ident(Overlapped)operator(;) + + reserved(do) operator({) + ident(gCurrentLoopTime) operator(=) ident(time)operator(()pre_constant(NULL)operator(\);) + comment(// Have some kind of strategy that will dequeue maybe up to 10 completions ) + comment(// without running the timers as long as they are available immediately.) + comment(// Otherwise in a busy server we're calling them every time through the loop.) + reserved(if) operator((!)ident(_RunTimers)operator((\)\)) + reserved(break)operator(;) + reserved(if) operator(()ident(GetQueuedCompletionStatus) operator(()ident(Iocp)operator(,) operator(&)ident(nBytes)operator(,) operator(&)ident(nCompletionKey)operator(,) operator(&)ident(Overlapped)operator(,) integer(1000)operator(\)\)) operator({) + operator(}) + ident(cerr) operator(<<) string<delimiter(")content(+)delimiter(")>operator(;) + operator(}) reserved(while) operator((!)ident(gTerminateSignalReceived)operator(\);) + + + comment(/* + while (true\) { + gCurrentLoopTime = time(NULL\); + if (!_RunTimers(\)\) + break; + _AddNewDescriptors(\); + if (!_RunOnce(\)\) + break; + if (gTerminateSignalReceived\) + break; + } + */) + + ident(HookControlC) operator(()pre_constant(false)operator(\);) +operator(}) + + +comment(/************************** +EventMachine_t::_RunTimers +**************************/) + +pre_type(bool) ident(EventMachine_t)operator(::)ident(_RunTimers)operator((\)) +operator({) + comment(// These are caller-defined timer handlers.) + comment(// Return T/F to indicate whether we should continue the main loop.) + comment(// We rely on the fact that multimaps sort by their keys to avoid) + comment(// inspecting the whole list every time we come here.) + comment(// Just keep inspecting and processing the list head until we hit) + comment(// one that hasn't expired yet.) + + reserved(while) operator(()pre_constant(true)operator(\)) operator({) + ident(multimap)operator(<)ident(time_t)operator(,)ident(Timer_t)operator(>::)ident(iterator) ident(i) operator(=) ident(Timers)operator(.)ident(begin)operator((\);) + reserved(if) operator(()ident(i) operator(==) ident(Timers)operator(.)ident(end)operator((\)\)) + reserved(break)operator(;) + reserved(if) operator(()ident(i)operator(->)ident(first) operator(>) ident(gCurrentLoopTime)operator(\)) + reserved(break)operator(;) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)) operator(()string<delimiter(")delimiter(")>operator(,) ident(EM_TIMER_FIRED)operator(,) ident(i)operator(->)ident(second)operator(.)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(i)operator(->)ident(second)operator(.)ident(GetBinding)operator((\))operator(.)ident(length)operator((\)\);) + ident(Timers)operator(.)ident(erase) operator(()ident(i)operator(\);) + operator(}) + reserved(return) pre_constant(true)operator(;) +operator(}) + + +comment(/*********************************** +EventMachine_t::InstallOneshotTimer +***********************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(InstallOneshotTimer) operator(()pre_type(int) ident(seconds)operator(\)) +operator({) + reserved(if) operator(()ident(Timers)operator(.)ident(size)operator((\)) operator(>) ident(MaxOutstandingTimers)operator(\)) + reserved(return) pre_constant(false)operator(;) + comment(// Don't use the global loop-time variable here, because we might) + comment(// get called before the main event machine is running.) + + ident(Timer_t) ident(t)operator(;) + ident(Timers)operator(.)ident(insert) operator(()ident(make_pair) operator(()ident(time)operator(()pre_constant(NULL)operator(\)) operator(+) ident(seconds)operator(,) ident(t)operator(\)\);) + reserved(return) ident(t)operator(.)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) +operator(}) + + +comment(/********************************** +EventMachine_t::OpenDatagramSocket +**********************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(OpenDatagramSocket) operator(()directive(const) pre_type(char) operator(*)ident(address)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + ident(cerr) operator(<<) string<delimiter(")content(OPEN DATAGRAM SOCKET)char(\\n)delimiter(")>operator(;) + reserved(return) string<delimiter(")content(Unimplemented)delimiter(")>operator(;) +operator(}) + + +comment(/******************************* +EventMachine_t::CreateTcpServer +*******************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(CreateTcpServer) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + comment(/* Create a TCP-acceptor (server\) socket and add it to the event machine. + * Return the binding of the new acceptor to the caller. + * This binding will be referenced when the new acceptor sends events + * to indicate accepted connections. + */) + + directive(const) pre_type(char) operator(*)ident(output_binding) operator(=) pre_constant(NULL)operator(;) + + reserved(struct) ident(sockaddr_in) ident(sin)operator(;) + + ident(SOCKET) ident(sd_accept) operator(=) ident(socket) operator(()ident(AF_INET)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd_accept) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + reserved(goto) ident(fail)operator(;) + operator(}) + + ident(memset) operator((&)ident(sin)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(sin)operator(\)\);) + ident(sin)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(INADDR_ANY)operator(;) + ident(sin)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + + reserved(if) operator(()ident(server) operator(&&) operator(*)ident(server)operator(\)) operator({) + ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(inet_addr) operator(()ident(server)operator(\);) + reserved(if) operator(()ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(==) ident(INADDR_NONE)operator(\)) operator({) + ident(hostent) operator(*)ident(hp) operator(=) ident(gethostbyname) operator(()ident(server)operator(\);) + reserved(if) operator(()ident(hp) operator(==) pre_constant(NULL)operator(\)) operator({) + comment(//__warning ("hostname not resolved: ", server\);) + reserved(goto) ident(fail)operator(;) + operator(}) + ident(sin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) operator((()ident(in_addr)operator(*\)()ident(hp)operator(->)ident(h_addr)operator(\)\)->)ident(s_addr)operator(;) + operator(}) + operator(}) + + + comment(// No need to set reuseaddr on Windows.) + + + reserved(if) operator(()ident(bind) operator(()ident(sd_accept)operator(,) operator(()reserved(struct) ident(sockaddr)operator(*\)&)ident(sin)operator(,) reserved(sizeof)operator(()ident(sin)operator(\)\)\)) operator({) + comment(//__warning ("binding failed"\);) + reserved(goto) ident(fail)operator(;) + operator(}) + + reserved(if) operator(()ident(listen) operator(()ident(sd_accept)operator(,) integer(100)operator(\)\)) operator({) + comment(//__warning ("listen failed"\);) + reserved(goto) ident(fail)operator(;) + operator(}) + + operator({) comment(// Looking good.) + ident(AcceptorDescriptor) operator(*)ident(ad) operator(=) reserved(new) ident(AcceptorDescriptor) operator(()local_variable(this)operator(,) ident(sd_accept)operator(\);) + reserved(if) operator((!)ident(ad)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to allocate acceptor)delimiter(")>operator(\);) + ident(Add) operator(()ident(ad)operator(\);) + ident(output_binding) operator(=) ident(ad)operator(->)ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\);) + + ident(CreateIoCompletionPort) operator((()ident(HANDLE)operator(\))ident(sd_accept)operator(,) ident(Iocp)operator(,) pre_constant(NULL)operator(,) integer(0)operator(\);) + ident(SOCKET) ident(sd) operator(=) ident(socket) operator(()ident(AF_INET)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + ident(CreateIoCompletionPort) operator((()ident(HANDLE)operator(\))ident(sd)operator(,) ident(Iocp)operator(,) pre_constant(NULL)operator(,) integer(0)operator(\);) + ident(AcceptEx) operator(()ident(sd_accept)operator(,) ident(sd)operator(,) + operator(}) + + reserved(return) ident(output_binding)operator(;) + + label(fail)operator(:) + reserved(if) operator(()ident(sd_accept) operator(!=) ident(INVALID_SOCKET)operator(\)) + ident(closesocket) operator(()ident(sd_accept)operator(\);) + reserved(return) pre_constant(NULL)operator(;) +operator(}) + + +comment(/******************************* +EventMachine_t::ConnectToServer +*******************************/) + +directive(const) pre_type(char) operator(*)ident(EventMachine_t)operator(::)ident(ConnectToServer) operator(()directive(const) pre_type(char) operator(*)ident(server)operator(,) pre_type(int) ident(port)operator(\)) +operator({) + reserved(if) operator((!)ident(server) operator(||) operator(!*)ident(server) operator(||) operator(!)ident(port)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + ident(sockaddr_in) ident(pin)operator(;) + pre_type(unsigned) pre_type(long) ident(HostAddr)operator(;) + + ident(HostAddr) operator(=) ident(inet_addr) operator(()ident(server)operator(\);) + reserved(if) operator(()ident(HostAddr) operator(==) ident(INADDR_NONE)operator(\)) operator({) + ident(hostent) operator(*)ident(hp) operator(=) ident(gethostbyname) operator(()ident(server)operator(\);) + reserved(if) operator((!)ident(hp)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + ident(HostAddr) operator(=) operator((()ident(in_addr)operator(*\)()ident(hp)operator(->)ident(h_addr)operator(\)\)->)ident(s_addr)operator(;) + operator(}) + + ident(memset) operator((&)ident(pin)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(pin)operator(\)\);) + ident(pin)operator(.)ident(sin_family) operator(=) ident(AF_INET)operator(;) + ident(pin)operator(.)ident(sin_addr)operator(.)ident(s_addr) operator(=) ident(HostAddr)operator(;) + ident(pin)operator(.)ident(sin_port) operator(=) ident(htons) operator(()ident(port)operator(\);) + + pre_type(int) ident(sd) operator(=) ident(socket) operator(()ident(AF_INET)operator(,) ident(SOCK_STREAM)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) + reserved(return) pre_constant(NULL)operator(;) + + + ident(LPOVERLAPPED) ident(olap) operator(=) operator(()ident(LPOVERLAPPED)operator(\)) ident(calloc) operator(()integer(1)operator(,) reserved(sizeof) operator(()ident(OVERLAPPED)operator(\)\);) + ident(cerr) operator(<<) string<delimiter(")content(I'm dying now)char(\\n)delimiter(")>operator(;) + reserved(throw) ident(runtime_error) operator(()string<delimiter(")content(UNIMPLEMENTED!!!)char(\\n)delimiter(")>operator(\);) + +operator(}) + + + +comment(/******************* +EventMachine_t::Add +*******************/) + +directive(void) ident(EventMachine_t)operator(::)ident(Add) operator(()ident(EventableDescriptor) operator(*)ident(ed)operator(\)) +operator({) + ident(cerr) operator(<<) string<delimiter(")content(ADD)char(\\n)delimiter(")>operator(;) +operator(}) + + + +preprocessor(#endif) comment(// OS_WIN32) + +comment(/***************************************************************************** + +$Id$ + +File: epoll.cpp +Date: 06Jun07 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + +preprocessor(#ifdef) ident(HAVE_EPOLL) + +preprocessor(#include) include("project.h") + +preprocessor(#endif) comment(// HAVE_EPOLL) + +comment(/***************************************************************************** + +$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $ + +File: mapper.cpp +Date: 02Jul07 + +Copyright (C\) 2007 by Francis Cianfrocca. All Rights Reserved. +Gmail: garbagecat10 + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + +comment(//////////////////////////////////////////////////////////////////////) +comment(// UNIX implementation) +comment(//////////////////////////////////////////////////////////////////////) + + +preprocessor(#ifdef) ident(OS_UNIX) + +preprocessor(#include) include(<sys/types.h>) +preprocessor(#include) include(<sys/stat.h>) +preprocessor(#include) include(<sys/mman.h>) +preprocessor(#include) include(<fcntl.h>) +preprocessor(#include) include(<errno.h>) + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include("unistd.h") +preprocessor(#include) include(<string>) +preprocessor(#include) include(<cstring>) +preprocessor(#include) include(<stdexcept>) +directive(using) reserved(namespace) ident(std)operator(;) + +preprocessor(#include) include("mapper.h") + +comment(/****************** +Mapper_t::Mapper_t +******************/) + +ident(Mapper_t)operator(::)ident(Mapper_t) operator(()directive(const) pre_type(string) operator(&)ident(filename)operator(\)) +operator({) + comment(/* We ASSUME we can open the file. + * (More precisely, we assume someone else checked before we got here.\) + */) + + ident(Fd) operator(=) ident(open) operator(()ident(filename)operator(.)ident(c_str)operator((\),) ident(O_RDONLY)operator(\);) + reserved(if) operator(()ident(Fd) operator(<) integer(0)operator(\)) + reserved(throw) ident(runtime_error) operator(()ident(strerror) operator(()ident(errno)operator(\)\);) + + reserved(struct) ident(stat) ident(st)operator(;) + reserved(if) operator(()ident(fstat) operator(()ident(Fd)operator(,) operator(&)ident(st)operator(\)\)) + reserved(throw) ident(runtime_error) operator(()ident(strerror) operator(()ident(errno)operator(\)\);) + ident(FileSize) operator(=) ident(st)operator(.)ident(st_size)operator(;) + + ident(MapPoint) operator(=) operator(()directive(const) pre_type(char)operator(*\)) ident(mmap) operator(()integer(0)operator(,) ident(FileSize)operator(,) ident(PROT_READ)operator(,) ident(MAP_SHARED)operator(,) ident(Fd)operator(,) integer(0)operator(\);) + reserved(if) operator(()ident(MapPoint) operator(==) ident(MAP_FAILED)operator(\)) + reserved(throw) ident(runtime_error) operator(()ident(strerror) operator(()ident(errno)operator(\)\);) +operator(}) + + +comment(/******************* +Mapper_t::~Mapper_t +*******************/) + +ident(Mapper_t)operator(::~)ident(Mapper_t)operator((\)) +operator({) + ident(Close)operator((\);) +operator(}) + + +comment(/*************** +Mapper_t::Close +***************/) + +directive(void) ident(Mapper_t)operator(::)ident(Close)operator((\)) +operator({) + comment(// Can be called multiple times.) + comment(// Calls to GetChunk are invalid after a call to Close.) + reserved(if) operator(()ident(MapPoint)operator(\)) operator({) + ident(munmap) operator((()directive(void)operator(*\))ident(MapPoint)operator(,) ident(FileSize)operator(\);) + ident(MapPoint) operator(=) pre_constant(NULL)operator(;) + operator(}) + reserved(if) operator(()ident(Fd) operator(>=) integer(0)operator(\)) operator({) + ident(close) operator(()ident(Fd)operator(\);) + ident(Fd) operator(=) operator(-)integer(1)operator(;) + operator(}) +operator(}) + +comment(/****************** +Mapper_t::GetChunk +******************/) + +directive(const) pre_type(char) operator(*)ident(Mapper_t)operator(::)ident(GetChunk) operator(()pre_type(unsigned) ident(start)operator(\)) +operator({) + reserved(return) ident(MapPoint) operator(+) ident(start)operator(;) +operator(}) + + + +preprocessor(#endif) comment(// OS_UNIX) + + +comment(//////////////////////////////////////////////////////////////////////) +comment(// WINDOWS implementation) +comment(//////////////////////////////////////////////////////////////////////) + +preprocessor(#ifdef) ident(OS_WIN32) + +preprocessor(#include) include(<windows.h>) + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<string>) +preprocessor(#include) include(<stdexcept>) +directive(using) reserved(namespace) ident(std)operator(;) + +preprocessor(#include) include("mapper.h") + +comment(/****************** +Mapper_t::Mapper_t +******************/) + +ident(Mapper_t)operator(::)ident(Mapper_t) operator(()directive(const) pre_type(string) operator(&)ident(filename)operator(\)) +operator({) + comment(/* We ASSUME we can open the file. + * (More precisely, we assume someone else checked before we got here.\) + */) + + ident(hFile) operator(=) ident(INVALID_HANDLE_VALUE)operator(;) + ident(hMapping) operator(=) pre_constant(NULL)operator(;) + ident(MapPoint) operator(=) pre_constant(NULL)operator(;) + ident(FileSize) operator(=) integer(0)operator(;) + + ident(hFile) operator(=) ident(CreateFile) operator(()ident(filename)operator(.)ident(c_str)operator((\),) ident(GENERIC_READ)operator(|)ident(GENERIC_WRITE)operator(,) ident(FILE_SHARE_DELETE)operator(|)ident(FILE_SHARE_READ)operator(|)ident(FILE_SHARE_WRITE)operator(,) pre_constant(NULL)operator(,) ident(OPEN_EXISTING)operator(,) ident(FILE_ATTRIBUTE_NORMAL)operator(,) pre_constant(NULL)operator(\);) + + reserved(if) operator(()ident(hFile) operator(==) ident(INVALID_HANDLE_VALUE)operator(\)) + reserved(throw) ident(runtime_error) operator(()string<delimiter(")content(File not found)delimiter(")>operator(\);) + + ident(BY_HANDLE_FILE_INFORMATION) ident(i)operator(;) + reserved(if) operator(()ident(GetFileInformationByHandle) operator(()ident(hFile)operator(,) operator(&)ident(i)operator(\)\)) + ident(FileSize) operator(=) ident(i)operator(.)ident(nFileSizeLow)operator(;) + + ident(hMapping) operator(=) ident(CreateFileMapping) operator(()ident(hFile)operator(,) pre_constant(NULL)operator(,) ident(PAGE_READWRITE)operator(,) integer(0)operator(,) integer(0)operator(,) pre_constant(NULL)operator(\);) + reserved(if) operator((!)ident(hMapping)operator(\)) + reserved(throw) ident(runtime_error) operator(()string<delimiter(")content(File not mapped)delimiter(")>operator(\);) + + ident(MapPoint) operator(=) operator(()directive(const) pre_type(char)operator(*\)) ident(MapViewOfFile) operator(()ident(hMapping)operator(,) ident(FILE_MAP_WRITE)operator(,) integer(0)operator(,) integer(0)operator(,) integer(0)operator(\);) + reserved(if) operator((!)ident(MapPoint)operator(\)) + reserved(throw) ident(runtime_error) operator(()string<delimiter(")content(Mappoint not read)delimiter(")>operator(\);) +operator(}) + + +comment(/******************* +Mapper_t::~Mapper_t +*******************/) + +ident(Mapper_t)operator(::~)ident(Mapper_t)operator((\)) +operator({) + ident(Close)operator((\);) +operator(}) + +comment(/*************** +Mapper_t::Close +***************/) + +directive(void) ident(Mapper_t)operator(::)ident(Close)operator((\)) +operator({) + comment(// Can be called multiple times.) + comment(// Calls to GetChunk are invalid after a call to Close.) + reserved(if) operator(()ident(MapPoint)operator(\)) operator({) + ident(UnmapViewOfFile) operator(()ident(MapPoint)operator(\);) + ident(MapPoint) operator(=) pre_constant(NULL)operator(;) + operator(}) + reserved(if) operator(()ident(hMapping) operator(!=) pre_constant(NULL)operator(\)) operator({) + ident(CloseHandle) operator(()ident(hMapping)operator(\);) + ident(hMapping) operator(=) pre_constant(NULL)operator(;) + operator(}) + reserved(if) operator(()ident(hFile) operator(!=) ident(INVALID_HANDLE_VALUE)operator(\)) operator({) + ident(CloseHandle) operator(()ident(hFile)operator(\);) + ident(hMapping) operator(=) ident(INVALID_HANDLE_VALUE)operator(;) + operator(}) +operator(}) + + +comment(/****************** +Mapper_t::GetChunk +******************/) + +directive(const) pre_type(char) operator(*)ident(Mapper_t)operator(::)ident(GetChunk) operator(()pre_type(unsigned) ident(start)operator(\)) +operator({) + reserved(return) ident(MapPoint) operator(+) ident(start)operator(;) +operator(}) + + + +preprocessor(#endif) comment(// OS_WINDOWS) +comment(/***************************************************************************** + +$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $ + +File: rubymain.cpp +Date: 02Jul07 + +Copyright (C\) 2007 by Francis Cianfrocca. All Rights Reserved. +Gmail: garbagecat10 + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<stdexcept>) +directive(using) reserved(namespace) ident(std)operator(;) + +preprocessor(#include) include(<ruby.h>) +preprocessor(#include) include("mapper.h") + +directive(static) ident(VALUE) ident(EmModule)operator(;) +directive(static) ident(VALUE) ident(FastFileReader)operator(;) +directive(static) ident(VALUE) ident(Mapper)operator(;) + + + +comment(/********* +mapper_dt +*********/) + +directive(static) directive(void) ident(mapper_dt) operator(()directive(void) operator(*)ident(ptr)operator(\)) +operator({) + reserved(if) operator(()ident(ptr)operator(\)) + reserved(delete) operator(()ident(Mapper_t)operator(*\)) ident(ptr)operator(;) +operator(}) + +comment(/********** +mapper_new +**********/) + +directive(static) ident(VALUE) ident(mapper_new) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(filename)operator(\)) +operator({) + ident(Mapper_t) operator(*)ident(m) operator(=) reserved(new) ident(Mapper_t) operator(()ident(StringValuePtr) operator(()ident(filename)operator(\)\);) + reserved(if) operator((!)ident(m)operator(\)) + ident(rb_raise) operator(()ident(rb_eException)operator(,) string<delimiter(")content(No Mapper Object)delimiter(")>operator(\);) + ident(VALUE) ident(v) operator(=) ident(Data_Wrap_Struct) operator(()ident(Mapper)operator(,) integer(0)operator(,) ident(mapper_dt)operator(,) operator(()directive(void)operator(*\))ident(m)operator(\);) + reserved(return) ident(v)operator(;) +operator(}) + + +comment(/**************** +mapper_get_chunk +****************/) + +directive(static) ident(VALUE) ident(mapper_get_chunk) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(start)operator(,) ident(VALUE) ident(length)operator(\)) +operator({) + ident(Mapper_t) operator(*)ident(m) operator(=) pre_constant(NULL)operator(;) + ident(Data_Get_Struct) operator(()ident(self)operator(,) ident(Mapper_t)operator(,) ident(m)operator(\);) + reserved(if) operator((!)ident(m)operator(\)) + ident(rb_raise) operator(()ident(rb_eException)operator(,) string<delimiter(")content(No Mapper Object)delimiter(")>operator(\);) + + comment(// TODO, what if some moron sends us a negative start value?) + pre_type(unsigned) ident(_start) operator(=) ident(NUM2INT) operator(()ident(start)operator(\);) + pre_type(unsigned) ident(_length) operator(=) ident(NUM2INT) operator(()ident(length)operator(\);) + reserved(if) operator((()ident(_start) operator(+) ident(_length)operator(\)) operator(>) ident(m)operator(->)ident(GetFileSize)operator((\)\)) + ident(rb_raise) operator(()ident(rb_eException)operator(,) string<delimiter(")content(Mapper Range Error)delimiter(")>operator(\);) + + directive(const) pre_type(char) operator(*)ident(chunk) operator(=) ident(m)operator(->)ident(GetChunk) operator(()ident(_start)operator(\);) + reserved(if) operator((!)ident(chunk)operator(\)) + ident(rb_raise) operator(()ident(rb_eException)operator(,) string<delimiter(")content(No Mapper Chunk)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new) operator(()ident(chunk)operator(,) ident(_length)operator(\);) +operator(}) + +comment(/************ +mapper_close +************/) + +directive(static) ident(VALUE) ident(mapper_close) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(Mapper_t) operator(*)ident(m) operator(=) pre_constant(NULL)operator(;) + ident(Data_Get_Struct) operator(()ident(self)operator(,) ident(Mapper_t)operator(,) ident(m)operator(\);) + reserved(if) operator((!)ident(m)operator(\)) + ident(rb_raise) operator(()ident(rb_eException)operator(,) string<delimiter(")content(No Mapper Object)delimiter(")>operator(\);) + ident(m)operator(->)ident(Close)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/*********** +mapper_size +***********/) + +directive(static) ident(VALUE) ident(mapper_size) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(Mapper_t) operator(*)ident(m) operator(=) pre_constant(NULL)operator(;) + ident(Data_Get_Struct) operator(()ident(self)operator(,) ident(Mapper_t)operator(,) ident(m)operator(\);) + reserved(if) operator((!)ident(m)operator(\)) + ident(rb_raise) operator(()ident(rb_eException)operator(,) string<delimiter(")content(No Mapper Object)delimiter(")>operator(\);) + reserved(return) ident(INT2NUM) operator(()ident(m)operator(->)ident(GetFileSize)operator((\)\);) +operator(}) + + +comment(/********************** +Init_fastfilereaderext +**********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(Init_fastfilereaderext)operator((\)) +operator({) + ident(EmModule) operator(=) ident(rb_define_module) operator(()string<delimiter(")content(EventMachine)delimiter(")>operator(\);) + ident(FastFileReader) operator(=) ident(rb_define_class_under) operator(()ident(EmModule)operator(,) string<delimiter(")content(FastFileReader)delimiter(")>operator(,) ident(rb_cObject)operator(\);) + ident(Mapper) operator(=) ident(rb_define_class_under) operator(()ident(FastFileReader)operator(,) string<delimiter(")content(Mapper)delimiter(")>operator(,) ident(rb_cObject)operator(\);) + + ident(rb_define_module_function) operator(()ident(Mapper)operator(,) string<delimiter(")content(new)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(mapper_new)operator(,) integer(1)operator(\);) + ident(rb_define_method) operator(()ident(Mapper)operator(,) string<delimiter(")content(size)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(mapper_size)operator(,) integer(0)operator(\);) + ident(rb_define_method) operator(()ident(Mapper)operator(,) string<delimiter(")content(close)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(mapper_close)operator(,) integer(0)operator(\);) + ident(rb_define_method) operator(()ident(Mapper)operator(,) string<delimiter(")content(get_chunk)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(mapper_get_chunk)operator(,) integer(2)operator(\);) +operator(}) + + + +comment(/***************************************************************************** + +$Id$ + +File: files.cpp +Date: 26Aug06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + + +comment(/****************************************** +FileStreamDescriptor::FileStreamDescriptor +******************************************/) + +ident(FileStreamDescriptor)operator(::)ident(FileStreamDescriptor) operator(()pre_type(int) ident(fd)operator(,) ident(EventMachine_t) operator(*)ident(em)operator(\):) + ident(EventableDescriptor) operator(()ident(fd)operator(,) ident(em)operator(\),) + ident(OutboundDataSize) operator(()integer(0)operator(\)) +operator({) +ident(cerr) operator(<<) string<delimiter(")content(#####)delimiter(")>operator(;) +operator(}) + + +comment(/******************************************* +FileStreamDescriptor::~FileStreamDescriptor +*******************************************/) + +ident(FileStreamDescriptor)operator(::~)ident(FileStreamDescriptor)operator((\)) +operator({) + comment(// Run down any stranded outbound data.) + reserved(for) operator(()ident(size_t) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(OutboundPages)operator(.)ident(size)operator((\);) ident(i)operator(++\)) + ident(OutboundPages)operator([)ident(i)operator(])operator(.)ident(Free)operator((\);) +operator(}) + + +comment(/************************** +FileStreamDescriptor::Read +**************************/) + +directive(void) ident(FileStreamDescriptor)operator(::)ident(Read)operator((\)) +operator({) +operator(}) + +comment(/*************************** +FileStreamDescriptor::Write +***************************/) + +directive(void) ident(FileStreamDescriptor)operator(::)ident(Write)operator((\)) +operator({) +operator(}) + + +comment(/******************************* +FileStreamDescriptor::Heartbeat +*******************************/) + +directive(void) ident(FileStreamDescriptor)operator(::)ident(Heartbeat)operator((\)) +operator({) +operator(}) + + +comment(/*********************************** +FileStreamDescriptor::SelectForRead +***********************************/) + +pre_type(bool) ident(FileStreamDescriptor)operator(::)ident(SelectForRead)operator((\)) +operator({) + ident(cerr) operator(<<) string<delimiter(")content(R?)delimiter(")>operator(;) + reserved(return) pre_constant(false)operator(;) +operator(}) + + +comment(/************************************ +FileStreamDescriptor::SelectForWrite +************************************/) + +pre_type(bool) ident(FileStreamDescriptor)operator(::)ident(SelectForWrite)operator((\)) +operator({) + ident(cerr) operator(<<) string<delimiter(")content(W?)delimiter(")>operator(;) + reserved(return) pre_constant(false)operator(;) +operator(}) + + +comment(/***************************************************************************** + +$Id$ + +File: kb.cpp +Date: 24Aug07 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + + +comment(/************************************** +KeyboardDescriptor::KeyboardDescriptor +**************************************/) + +ident(KeyboardDescriptor)operator(::)ident(KeyboardDescriptor) operator(()ident(EventMachine_t) operator(*)ident(parent_em)operator(\):) + ident(EventableDescriptor) operator(()integer(0)operator(,) ident(parent_em)operator(\),) + ident(bReadAttemptedAfterClose) operator(()pre_constant(false)operator(\),) + ident(LastIo) operator(()ident(gCurrentLoopTime)operator(\),) + ident(InactivityTimeout) operator(()integer(0)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLIN)operator(;) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/*************************************** +KeyboardDescriptor::~KeyboardDescriptor +***************************************/) + +ident(KeyboardDescriptor)operator(::~)ident(KeyboardDescriptor)operator((\)) +operator({) +operator(}) + + +comment(/************************* +KeyboardDescriptor::Write +*************************/) + +directive(void) ident(KeyboardDescriptor)operator(::)ident(Write)operator((\)) +operator({) + comment(// Why are we here?) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad code path in keyboard handler)delimiter(")>operator(\);) +operator(}) + + +comment(/***************************** +KeyboardDescriptor::Heartbeat +*****************************/) + +directive(void) ident(KeyboardDescriptor)operator(::)ident(Heartbeat)operator((\)) +operator({) + comment(// no-op) +operator(}) + + +comment(/************************ +KeyboardDescriptor::Read +************************/) + +directive(void) ident(KeyboardDescriptor)operator(::)ident(Read)operator((\)) +operator({) + pre_type(char) ident(c)operator(;) + ident(read) operator(()ident(GetSocket)operator((\),) operator(&)ident(c)operator(,) integer(1)operator(\);) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_READ)operator(,) operator(&)ident(c)operator(,) integer(1)operator(\);) +operator(}) + + + + +comment(#if 0 +/****************************** +PipeDescriptor::PipeDescriptor +******************************/ + +PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em\): + EventableDescriptor (fd, parent_em\), + bReadAttemptedAfterClose (false\), + LastIo (gCurrentLoopTime\), + InactivityTimeout (0\), + OutboundDataSize (0\), + SubprocessPid (subpid\) +{ + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif +} + + +/******************************* +PipeDescriptor::~PipeDescriptor +*******************************/ + +PipeDescriptor::~PipeDescriptor(\) +{ + // Run down any stranded outbound data. + for (size_t i=0; i < OutboundPages.size(\); i++\) + OutboundPages[i].Free(\); + + /* As a virtual destructor, we come here before the base-class + * destructor that closes our file-descriptor. + * We have to make sure the subprocess goes down (if it's not + * already down\) and we have to reap the zombie. + * + * This implementation is PROVISIONAL and will surely be improved. + * The intention here is that we never block, hence the highly + * undesirable sleeps. But if we can't reap the subprocess even + * after sending it SIGKILL, then something is wrong and we + * throw a fatal exception, which is also not something we should + * be doing. + * + * Eventually the right thing to do will be to have the reactor + * core respond to SIGCHLD by chaining a handler on top of the + * one Ruby may have installed, and dealing with a list of dead + * children that are pending cleanup. + * + * Since we want to have a signal processor integrated into the + * client-visible API, let's wait until that is done before cleaning + * this up. + */ + + struct timespec req = {0, 10000000}; + kill (SubprocessPid, SIGTERM\); + nanosleep (&req, NULL\); + if (waitpid (SubprocessPid, NULL, WNOHANG\) == 0\) { + kill (SubprocessPid, SIGKILL\); + nanosleep (&req, NULL\); + if (waitpid (SubprocessPid, NULL, WNOHANG\) == 0\) + throw std::runtime_error ("unable to reap subprocess"\); + } +} + + + +/******************** +PipeDescriptor::Read +********************/ + +void PipeDescriptor::Read(\) +{ + int sd = GetSocket(\); + if (sd == INVALID_SOCKET\) { + assert (!bReadAttemptedAfterClose\); + bReadAttemptedAfterClose = true; + return; + } + + LastIo = gCurrentLoopTime; + + int total_bytes_read = 0; + char readbuffer [16 * 1024]; + + for (int i=0; i < 10; i++\) { + // Don't read just one buffer and then move on. This is faster + // if there is a lot of incoming. + // But don't read indefinitely. Give other sockets a chance to run. + // NOTICE, we're reading one less than the buffer size. + // That's so we can put a guard byte at the end of what we send + // to user code. + // Use read instead of recv, which on Linux gives a "socket operation + // on nonsocket" error. + + + int r = read (sd, readbuffer, sizeof(readbuffer\) - 1\); + //cerr << "<R:" << r << ">"; + + if (r > 0\) { + total_bytes_read += r; + LastRead = gCurrentLoopTime; + + // Add a null-terminator at the the end of the buffer + // that we will send to the callback. + // DO NOT EVER CHANGE THIS. We want to explicitly allow users + // to be able to depend on this behavior, so they will have + // the option to do some things faster. Additionally it's + // a security guard against buffer overflows. + readbuffer [r] = 0; + if (EventCallback\) + (*EventCallback\)(GetBinding(\).c_str(\), EM_CONNECTION_READ, readbuffer, r\); + } + else if (r == 0\) { + break; + } + else { + // Basically a would-block, meaning we've read everything there is to read. + break; + } + + } + + + if (total_bytes_read == 0\) { + // If we read no data on a socket that selected readable, + // it generally means the other end closed the connection gracefully. + ScheduleClose (false\); + //bCloseNow = true; + } + +} + +/********************* +PipeDescriptor::Write +*********************/ + +void PipeDescriptor::Write(\) +{ + int sd = GetSocket(\); + assert (sd != INVALID_SOCKET\); + + LastIo = gCurrentLoopTime; + char output_buffer [16 * 1024]; + size_t nbytes = 0; + + while ((OutboundPages.size(\) > 0\) && (nbytes < sizeof(output_buffer\)\)\) { + OutboundPage *op = &(OutboundPages[0]\); + if ((nbytes + op->Length - op->Offset\) < sizeof (output_buffer\)\) { + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset\); + nbytes += (op->Length - op->Offset\); + op->Free(\); + OutboundPages.pop_front(\); + } + else { + int len = sizeof(output_buffer\) - nbytes; + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len\); + op->Offset += len; + nbytes += len; + } + } + + // We should never have gotten here if there were no data to write, + // so assert that as a sanity check. + // Don't bother to make sure nbytes is less than output_buffer because + // if it were we probably would have crashed already. + assert (nbytes > 0\); + + assert (GetSocket(\) != INVALID_SOCKET\); + int bytes_written = write (GetSocket(\), output_buffer, nbytes\); + + if (bytes_written > 0\) { + OutboundDataSize -= bytes_written; + if ((size_t\)bytes_written < nbytes\) { + int len = nbytes - bytes_written; + char *buffer = (char*\) malloc (len + 1\); + if (!buffer\) + throw std::runtime_error ("bad alloc throwing back data"\); + memcpy (buffer, output_buffer + bytes_written, len\); + buffer [len] = 0; + OutboundPages.push_front (OutboundPage (buffer, len\)\); + } + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | (SelectForWrite(\) ? EPOLLOUT : 0\)\); + assert (MyEventMachine\); + MyEventMachine->Modify (this\); + #endif + } + else { + #ifdef OS_UNIX + if ((errno != EINPROGRESS\) && (errno != EWOULDBLOCK\) && (errno != EINTR\)\) + #endif + #ifdef OS_WIN32 + if ((errno != WSAEINPROGRESS\) && (errno != WSAEWOULDBLOCK\)\) + #endif + Close(\); + } +} + + +/************************* +PipeDescriptor::Heartbeat +*************************/ + +void PipeDescriptor::Heartbeat(\) +{ + // If an inactivity timeout is defined, then check for it. + if (InactivityTimeout && ((gCurrentLoopTime - LastIo\) >= InactivityTimeout\)\) + ScheduleClose (false\); + //bCloseNow = true; +} + + +/***************************** +PipeDescriptor::SelectForRead +*****************************/ + +bool PipeDescriptor::SelectForRead(\) +{ + /* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */ + return true; +} + +/****************************** +PipeDescriptor::SelectForWrite +******************************/ + +bool PipeDescriptor::SelectForWrite(\) +{ + /* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */ + return (GetOutboundDataSize(\) > 0\); +} + + + + +/******************************** +PipeDescriptor::SendOutboundData +********************************/ + +int PipeDescriptor::SendOutboundData (const char *data, int length\) +{ + //if (bCloseNow || bCloseAfterWriting\) + if (IsCloseScheduled(\)\) + return 0; + + if (!data && (length > 0\)\) + throw std::runtime_error ("bad outbound data"\); + char *buffer = (char *\) malloc (length + 1\); + if (!buffer\) + throw std::runtime_error ("no allocation for outbound data"\); + memcpy (buffer, data, length\); + buffer [length] = 0; + OutboundPages.push_back (OutboundPage (buffer, length\)\); + OutboundDataSize += length; + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | EPOLLOUT\); + assert (MyEventMachine\); + MyEventMachine->Modify (this\); + #endif + return length; +} + +/******************************** +PipeDescriptor::GetSubprocessPid +********************************/ + +bool PipeDescriptor::GetSubprocessPid (pid_t *pid\) +{ + bool ok = false; + if (pid && (SubprocessPid > 0\)\) { + *pid = SubprocessPid; + ok = true; + } + return ok; +} + +#endif ) + +comment(/***************************************************************************** + +$Id$ + +File: page.cpp +Date: 30Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + +preprocessor(#include) include("project.h") + + +comment(/****************** +PageList::PageList +******************/) + +ident(PageList)operator(::)ident(PageList)operator((\)) +operator({) +operator(}) + + +comment(/******************* +PageList::~PageList +*******************/) + +ident(PageList)operator(::~)ident(PageList)operator((\)) +operator({) + reserved(while) operator(()ident(HasPages)operator((\)\)) + ident(PopFront)operator((\);) +operator(}) + + +comment(/*************** +PageList::Front +***************/) + +directive(void) ident(PageList)operator(::)ident(Front) operator(()directive(const) pre_type(char) operator(**)ident(page)operator(,) pre_type(int) operator(*)ident(length)operator(\)) +operator({) + ident(assert) operator(()ident(page) operator(&&) ident(length)operator(\);) + + reserved(if) operator(()ident(HasPages)operator((\)\)) operator({) + ident(Page) ident(p) operator(=) ident(Pages)operator(.)ident(front)operator((\);) + operator(*)ident(page) operator(=) ident(p)operator(.)ident(Buffer)operator(;) + operator(*)ident(length) operator(=) ident(p)operator(.)ident(Size)operator(;) + operator(}) + reserved(else) operator({) + operator(*)ident(page) operator(=) pre_constant(NULL)operator(;) + operator(*)ident(length) operator(=) integer(0)operator(;) + operator(}) +operator(}) + + +comment(/****************** +PageList::PopFront +******************/) + +directive(void) ident(PageList)operator(::)ident(PopFront)operator((\)) +operator({) + reserved(if) operator(()ident(HasPages)operator((\)\)) operator({) + ident(Page) ident(p) operator(=) ident(Pages)operator(.)ident(front)operator((\);) + ident(Pages)operator(.)ident(pop_front)operator((\);) + reserved(if) operator(()ident(p)operator(.)ident(Buffer)operator(\)) + ident(free) operator((()directive(void)operator(*\))ident(p)operator(.)ident(Buffer)operator(\);) + operator(}) +operator(}) + + +comment(/****************** +PageList::HasPages +******************/) + +pre_type(bool) ident(PageList)operator(::)ident(HasPages)operator((\)) +operator({) + reserved(return) operator(()ident(Pages)operator(.)ident(size)operator((\)) operator(>) integer(0)operator(\)) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(;) +operator(}) + + +comment(/************** +PageList::Push +**************/) + +directive(void) ident(PageList)operator(::)ident(Push) operator(()directive(const) pre_type(char) operator(*)ident(buf)operator(,) pre_type(int) ident(size)operator(\)) +operator({) + reserved(if) operator(()ident(buf) operator(&&) operator(()ident(size) operator(>) integer(0)operator(\)\)) operator({) + pre_type(char) operator(*)ident(copy) operator(=) operator(()pre_type(char)operator(*\)) ident(malloc) operator(()ident(size)operator(\);) + reserved(if) operator((!)ident(copy)operator(\)) + reserved(throw) ident(runtime_error) operator(()string<delimiter(")content(no memory in pagelist)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(copy)operator(,) ident(buf)operator(,) ident(size)operator(\);) + ident(Pages)operator(.)ident(push_back) operator(()ident(Page) operator(()ident(copy)operator(,) ident(size)operator(\)\);) + operator(}) +operator(}) + + + + + +comment(/***************************************************************************** + +$Id$ + +File: pipe.cpp +Date: 30May07 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + + +preprocessor(#ifdef) ident(OS_UNIX) +comment(// THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.) + +comment(/****************************** +PipeDescriptor::PipeDescriptor +******************************/) + +ident(PipeDescriptor)operator(::)ident(PipeDescriptor) operator(()pre_type(int) ident(fd)operator(,) ident(pid_t) ident(subpid)operator(,) ident(EventMachine_t) operator(*)ident(parent_em)operator(\):) + ident(EventableDescriptor) operator(()ident(fd)operator(,) ident(parent_em)operator(\),) + ident(bReadAttemptedAfterClose) operator(()pre_constant(false)operator(\),) + ident(LastIo) operator(()ident(gCurrentLoopTime)operator(\),) + ident(InactivityTimeout) operator(()integer(0)operator(\),) + ident(OutboundDataSize) operator(()integer(0)operator(\),) + ident(SubprocessPid) operator(()ident(subpid)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) ident(EPOLLIN)operator(;) + preprocessor(#endif) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + ident(MyEventMachine)operator(->)ident(ArmKqueueReader) operator(()local_variable(this)operator(\);) + preprocessor(#endif) +operator(}) + + +comment(/******************************* +PipeDescriptor::~PipeDescriptor +*******************************/) + +ident(PipeDescriptor)operator(::~)ident(PipeDescriptor)operator((\)) +operator({) + comment(// Run down any stranded outbound data.) + reserved(for) operator(()ident(size_t) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(OutboundPages)operator(.)ident(size)operator((\);) ident(i)operator(++\)) + ident(OutboundPages)operator([)ident(i)operator(])operator(.)ident(Free)operator((\);) + + comment(/* As a virtual destructor, we come here before the base-class + * destructor that closes our file-descriptor. + * We have to make sure the subprocess goes down (if it's not + * already down\) and we have to reap the zombie. + * + * This implementation is PROVISIONAL and will surely be improved. + * The intention here is that we never block, hence the highly + * undesirable sleeps. But if we can't reap the subprocess even + * after sending it SIGKILL, then something is wrong and we + * throw a fatal exception, which is also not something we should + * be doing. + * + * Eventually the right thing to do will be to have the reactor + * core respond to SIGCHLD by chaining a handler on top of the + * one Ruby may have installed, and dealing with a list of dead + * children that are pending cleanup. + * + * Since we want to have a signal processor integrated into the + * client-visible API, let's wait until that is done before cleaning + * this up. + * + * Added a very ugly hack to support passing the subprocess's exit + * status to the user. It only makes logical sense for user code to access + * the subprocess exit status in the unbind callback. But unbind is called + * back during the EventableDescriptor destructor. So by that time there's + * no way to call back this object through an object binding, because it's + * already been cleaned up. We might have added a parameter to the unbind + * callback, but that would probably break a huge amount of existing code. + * So the hack-solution is to define an instance variable in the EventMachine + * object and stick the exit status in there, where it can easily be accessed + * with an accessor visible to user code. + * User code should ONLY access the exit status from within the unbind callback. + * Otherwise there's no guarantee it'll be valid. + * This hack won't make it impossible to run multiple EventMachines in a single + * process, but it will make it impossible to reliably nest unbind calls + * within other unbind calls. (Not sure if that's even possible.\) + */) + + ident(assert) operator(()ident(MyEventMachine)operator(\);) + + comment(// check if the process is already dead) + reserved(if) operator(()ident(waitpid) operator(()ident(SubprocessPid)operator(,) operator(&()ident(MyEventMachine)operator(->)ident(SubprocessExitStatus)operator(\),) ident(WNOHANG)operator(\)) operator(==) integer(0)operator(\)) operator({) + ident(kill) operator(()ident(SubprocessPid)operator(,) ident(SIGTERM)operator(\);) + comment(// wait 0.25s for process to die) + reserved(struct) ident(timespec) ident(req) operator(=) operator({)integer(0)operator(,) integer(250000000)operator(};) + ident(nanosleep) operator((&)ident(req)operator(,) pre_constant(NULL)operator(\);) + reserved(if) operator(()ident(waitpid) operator(()ident(SubprocessPid)operator(,) operator(&()ident(MyEventMachine)operator(->)ident(SubprocessExitStatus)operator(\),) ident(WNOHANG)operator(\)) operator(==) integer(0)operator(\)) operator({) + ident(kill) operator(()ident(SubprocessPid)operator(,) ident(SIGKILL)operator(\);) + comment(// wait 0.5s for process to die) + reserved(struct) ident(timespec) ident(req) operator(=) operator({)integer(0)operator(,) integer(500000000)operator(};) + ident(nanosleep) operator((&)ident(req)operator(,) pre_constant(NULL)operator(\);) + reserved(if) operator(()ident(waitpid) operator(()ident(SubprocessPid)operator(,) operator(&()ident(MyEventMachine)operator(->)ident(SubprocessExitStatus)operator(\),) ident(WNOHANG)operator(\)) operator(==) integer(0)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(unable to reap subprocess)delimiter(")>operator(\);) + operator(}) + operator(}) +operator(}) + + + +comment(/******************** +PipeDescriptor::Read +********************/) + +directive(void) ident(PipeDescriptor)operator(::)ident(Read)operator((\)) +operator({) + pre_type(int) ident(sd) operator(=) ident(GetSocket)operator((\);) + reserved(if) operator(()ident(sd) operator(==) ident(INVALID_SOCKET)operator(\)) operator({) + ident(assert) operator((!)ident(bReadAttemptedAfterClose)operator(\);) + ident(bReadAttemptedAfterClose) operator(=) pre_constant(true)operator(;) + reserved(return)operator(;) + operator(}) + + ident(LastIo) operator(=) ident(gCurrentLoopTime)operator(;) + + pre_type(int) ident(total_bytes_read) operator(=) integer(0)operator(;) + pre_type(char) ident(readbuffer) operator([)integer(16) operator(*) integer(1024)operator(];) + + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) integer(10)operator(;) ident(i)operator(++\)) operator({) + comment(// Don't read just one buffer and then move on. This is faster) + comment(// if there is a lot of incoming.) + comment(// But don't read indefinitely. Give other sockets a chance to run.) + comment(// NOTICE, we're reading one less than the buffer size.) + comment(// That's so we can put a guard byte at the end of what we send) + comment(// to user code.) + comment(// Use read instead of recv, which on Linux gives a "socket operation) + comment(// on nonsocket" error.) + + + pre_type(int) ident(r) operator(=) ident(read) operator(()ident(sd)operator(,) ident(readbuffer)operator(,) reserved(sizeof)operator(()ident(readbuffer)operator(\)) operator(-) integer(1)operator(\);) + comment(//cerr << "<R:" << r << ">";) + + reserved(if) operator(()ident(r) operator(>) integer(0)operator(\)) operator({) + ident(total_bytes_read) operator(+=) ident(r)operator(;) + ident(LastRead) operator(=) ident(gCurrentLoopTime)operator(;) + + comment(// Add a null-terminator at the the end of the buffer) + comment(// that we will send to the callback.) + comment(// DO NOT EVER CHANGE THIS. We want to explicitly allow users) + comment(// to be able to depend on this behavior, so they will have) + comment(// the option to do some things faster. Additionally it's) + comment(// a security guard against buffer overflows.) + ident(readbuffer) operator([)ident(r)operator(]) operator(=) integer(0)operator(;) + reserved(if) operator(()ident(EventCallback)operator(\)) + operator((*)ident(EventCallback)operator(\)()ident(GetBinding)operator((\))operator(.)ident(c_str)operator((\),) ident(EM_CONNECTION_READ)operator(,) ident(readbuffer)operator(,) ident(r)operator(\);) + operator(}) + reserved(else) reserved(if) operator(()ident(r) operator(==) integer(0)operator(\)) operator({) + reserved(break)operator(;) + operator(}) + reserved(else) operator({) + comment(// Basically a would-block, meaning we've read everything there is to read.) + reserved(break)operator(;) + operator(}) + + operator(}) + + + reserved(if) operator(()ident(total_bytes_read) operator(==) integer(0)operator(\)) operator({) + comment(// If we read no data on a socket that selected readable,) + comment(// it generally means the other end closed the connection gracefully.) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) + operator(}) + +operator(}) + +comment(/********************* +PipeDescriptor::Write +*********************/) + +directive(void) ident(PipeDescriptor)operator(::)ident(Write)operator((\)) +operator({) + pre_type(int) ident(sd) operator(=) ident(GetSocket)operator((\);) + ident(assert) operator(()ident(sd) operator(!=) ident(INVALID_SOCKET)operator(\);) + + ident(LastIo) operator(=) ident(gCurrentLoopTime)operator(;) + pre_type(char) ident(output_buffer) operator([)integer(16) operator(*) integer(1024)operator(];) + ident(size_t) ident(nbytes) operator(=) integer(0)operator(;) + + reserved(while) operator((()ident(OutboundPages)operator(.)ident(size)operator((\)) operator(>) integer(0)operator(\)) operator(&&) operator(()ident(nbytes) operator(<) reserved(sizeof)operator(()ident(output_buffer)operator(\)\)\)) operator({) + ident(OutboundPage) operator(*)ident(op) operator(=) operator(&()ident(OutboundPages)operator([)integer(0)operator(]\);) + reserved(if) operator((()ident(nbytes) operator(+) ident(op)operator(->)ident(Length) operator(-) ident(op)operator(->)ident(Offset)operator(\)) operator(<) reserved(sizeof) operator(()ident(output_buffer)operator(\)\)) operator({) + ident(memcpy) operator(()ident(output_buffer) operator(+) ident(nbytes)operator(,) ident(op)operator(->)ident(Buffer) operator(+) ident(op)operator(->)ident(Offset)operator(,) ident(op)operator(->)ident(Length) operator(-) ident(op)operator(->)ident(Offset)operator(\);) + ident(nbytes) operator(+=) operator(()ident(op)operator(->)ident(Length) operator(-) ident(op)operator(->)ident(Offset)operator(\);) + ident(op)operator(->)ident(Free)operator((\);) + ident(OutboundPages)operator(.)ident(pop_front)operator((\);) + operator(}) + reserved(else) operator({) + pre_type(int) ident(len) operator(=) reserved(sizeof)operator(()ident(output_buffer)operator(\)) operator(-) ident(nbytes)operator(;) + ident(memcpy) operator(()ident(output_buffer) operator(+) ident(nbytes)operator(,) ident(op)operator(->)ident(Buffer) operator(+) ident(op)operator(->)ident(Offset)operator(,) ident(len)operator(\);) + ident(op)operator(->)ident(Offset) operator(+=) ident(len)operator(;) + ident(nbytes) operator(+=) ident(len)operator(;) + operator(}) + operator(}) + + comment(// We should never have gotten here if there were no data to write,) + comment(// so assert that as a sanity check.) + comment(// Don't bother to make sure nbytes is less than output_buffer because) + comment(// if it were we probably would have crashed already.) + ident(assert) operator(()ident(nbytes) operator(>) integer(0)operator(\);) + + ident(assert) operator(()ident(GetSocket)operator((\)) operator(!=) ident(INVALID_SOCKET)operator(\);) + pre_type(int) ident(bytes_written) operator(=) ident(write) operator(()ident(GetSocket)operator((\),) ident(output_buffer)operator(,) ident(nbytes)operator(\);) + + reserved(if) operator(()ident(bytes_written) operator(>) integer(0)operator(\)) operator({) + ident(OutboundDataSize) operator(-=) ident(bytes_written)operator(;) + reserved(if) operator((()ident(size_t)operator(\))ident(bytes_written) operator(<) ident(nbytes)operator(\)) operator({) + pre_type(int) ident(len) operator(=) ident(nbytes) operator(-) ident(bytes_written)operator(;) + pre_type(char) operator(*)ident(buffer) operator(=) operator(()pre_type(char)operator(*\)) ident(malloc) operator(()ident(len) operator(+) integer(1)operator(\);) + reserved(if) operator((!)ident(buffer)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad alloc throwing back data)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(buffer)operator(,) ident(output_buffer) operator(+) ident(bytes_written)operator(,) ident(len)operator(\);) + ident(buffer) operator([)ident(len)operator(]) operator(=) integer(0)operator(;) + ident(OutboundPages)operator(.)ident(push_front) operator(()ident(OutboundPage) operator(()ident(buffer)operator(,) ident(len)operator(\)\);) + operator(}) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) operator(()ident(SelectForWrite)operator((\)) operator(?) ident(EPOLLOUT) operator(:) integer(0)operator(\)\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + operator(}) + reserved(else) operator({) + preprocessor(#ifdef) ident(OS_UNIX) + reserved(if) operator((()ident(errno) operator(!=) ident(EINPROGRESS)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(EWOULDBLOCK)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(EINTR)operator(\)\)) + preprocessor(#endif) + preprocessor(#ifdef) ident(OS_WIN32) + reserved(if) operator((()ident(errno) operator(!=) ident(WSAEINPROGRESS)operator(\)) operator(&&) operator(()ident(errno) operator(!=) ident(WSAEWOULDBLOCK)operator(\)\)) + preprocessor(#endif) + ident(Close)operator((\);) + operator(}) +operator(}) + + +comment(/************************* +PipeDescriptor::Heartbeat +*************************/) + +directive(void) ident(PipeDescriptor)operator(::)ident(Heartbeat)operator((\)) +operator({) + comment(// If an inactivity timeout is defined, then check for it.) + reserved(if) operator(()ident(InactivityTimeout) operator(&&) operator((()ident(gCurrentLoopTime) operator(-) ident(LastIo)operator(\)) operator(>=) ident(InactivityTimeout)operator(\)\)) + ident(ScheduleClose) operator(()pre_constant(false)operator(\);) + comment(//bCloseNow = true;) +operator(}) + + +comment(/***************************** +PipeDescriptor::SelectForRead +*****************************/) + +pre_type(bool) ident(PipeDescriptor)operator(::)ident(SelectForRead)operator((\)) +operator({) + comment(/* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */) + reserved(return) pre_constant(true)operator(;) +operator(}) + +comment(/****************************** +PipeDescriptor::SelectForWrite +******************************/) + +pre_type(bool) ident(PipeDescriptor)operator(::)ident(SelectForWrite)operator((\)) +operator({) + comment(/* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */) + reserved(return) operator(()ident(GetOutboundDataSize)operator((\)) operator(>) integer(0)operator(\);) +operator(}) + + + + +comment(/******************************** +PipeDescriptor::SendOutboundData +********************************/) + +pre_type(int) ident(PipeDescriptor)operator(::)ident(SendOutboundData) operator(()directive(const) pre_type(char) operator(*)ident(data)operator(,) pre_type(int) ident(length)operator(\)) +operator({) + comment(//if (bCloseNow || bCloseAfterWriting\)) + reserved(if) operator(()ident(IsCloseScheduled)operator((\)\)) + reserved(return) integer(0)operator(;) + + reserved(if) operator((!)ident(data) operator(&&) operator(()ident(length) operator(>) integer(0)operator(\)\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(bad outbound data)delimiter(")>operator(\);) + pre_type(char) operator(*)ident(buffer) operator(=) operator(()pre_type(char) operator(*\)) ident(malloc) operator(()ident(length) operator(+) integer(1)operator(\);) + reserved(if) operator((!)ident(buffer)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no allocation for outbound data)delimiter(")>operator(\);) + ident(memcpy) operator(()ident(buffer)operator(,) ident(data)operator(,) ident(length)operator(\);) + ident(buffer) operator([)ident(length)operator(]) operator(=) integer(0)operator(;) + ident(OutboundPages)operator(.)ident(push_back) operator(()ident(OutboundPage) operator(()ident(buffer)operator(,) ident(length)operator(\)\);) + ident(OutboundDataSize) operator(+=) ident(length)operator(;) + preprocessor(#ifdef) ident(HAVE_EPOLL) + ident(EpollEvent)operator(.)ident(events) operator(=) operator(()ident(EPOLLIN) operator(|) ident(EPOLLOUT)operator(\);) + ident(assert) operator(()ident(MyEventMachine)operator(\);) + ident(MyEventMachine)operator(->)ident(Modify) operator(()local_variable(this)operator(\);) + preprocessor(#endif) + reserved(return) ident(length)operator(;) +operator(}) + +comment(/******************************** +PipeDescriptor::GetSubprocessPid +********************************/) + +pre_type(bool) ident(PipeDescriptor)operator(::)ident(GetSubprocessPid) operator(()ident(pid_t) operator(*)ident(pid)operator(\)) +operator({) + pre_type(bool) ident(ok) operator(=) pre_constant(false)operator(;) + reserved(if) operator(()ident(pid) operator(&&) operator(()ident(SubprocessPid) operator(>) integer(0)operator(\)\)) operator({) + operator(*)ident(pid) operator(=) ident(SubprocessPid)operator(;) + ident(ok) operator(=) pre_constant(true)operator(;) + operator(}) + reserved(return) ident(ok)operator(;) +operator(}) + + +preprocessor(#endif) comment(// OS_UNIX) + +comment(/***************************************************************************** + +$Id$ + +File: rubymain.cpp +Date: 06Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") +preprocessor(#include) include("eventmachine.h") +preprocessor(#include) include(<ruby.h>) + + + +comment(/******* +Statics +*******/) + +directive(static) ident(VALUE) ident(EmModule)operator(;) +directive(static) ident(VALUE) ident(EmConnection)operator(;) + +directive(static) ident(VALUE) ident(Intern_at_signature)operator(;) +directive(static) ident(VALUE) ident(Intern_at_timers)operator(;) +directive(static) ident(VALUE) ident(Intern_at_conns)operator(;) +directive(static) ident(VALUE) ident(Intern_event_callback)operator(;) +directive(static) ident(VALUE) ident(Intern_run_deferred_callbacks)operator(;) +directive(static) ident(VALUE) ident(Intern_delete)operator(;) +directive(static) ident(VALUE) ident(Intern_call)operator(;) +directive(static) ident(VALUE) ident(Intern_receive_data)operator(;) + +directive(static) ident(VALUE) ident(Intern_notify_readable)operator(;) +directive(static) ident(VALUE) ident(Intern_notify_writable)operator(;) + +comment(/**************** +t_event_callback +****************/) + +directive(static) directive(void) ident(event_callback) operator(()directive(const) pre_type(char) operator(*)ident(a1)operator(,) pre_type(int) ident(a2)operator(,) directive(const) pre_type(char) operator(*)ident(a3)operator(,) pre_type(int) ident(a4)operator(\)) +operator({) + reserved(if) operator(()ident(a2) operator(==) ident(EM_CONNECTION_READ)operator(\)) operator({) + ident(VALUE) ident(t) operator(=) ident(rb_ivar_get) operator(()ident(EmModule)operator(,) ident(Intern_at_conns)operator(\);) + ident(VALUE) ident(q) operator(=) ident(rb_hash_aref) operator(()ident(t)operator(,) ident(rb_str_new2)operator(()ident(a1)operator(\)\);) + reserved(if) operator(()ident(q) operator(==) ident(Qnil)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no connection)delimiter(")>operator(\);) + ident(rb_funcall) operator(()ident(q)operator(,) ident(Intern_receive_data)operator(,) integer(1)operator(,) ident(rb_str_new) operator(()ident(a3)operator(,) ident(a4)operator(\)\);) + operator(}) + reserved(else) reserved(if) operator(()ident(a2) operator(==) ident(EM_CONNECTION_NOTIFY_READABLE)operator(\)) operator({) + ident(VALUE) ident(t) operator(=) ident(rb_ivar_get) operator(()ident(EmModule)operator(,) ident(Intern_at_conns)operator(\);) + ident(VALUE) ident(q) operator(=) ident(rb_hash_aref) operator(()ident(t)operator(,) ident(rb_str_new2)operator(()ident(a1)operator(\)\);) + reserved(if) operator(()ident(q) operator(==) ident(Qnil)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no connection)delimiter(")>operator(\);) + ident(rb_funcall) operator(()ident(q)operator(,) ident(Intern_notify_readable)operator(,) integer(0)operator(\);) + operator(}) + reserved(else) reserved(if) operator(()ident(a2) operator(==) ident(EM_CONNECTION_NOTIFY_WRITABLE)operator(\)) operator({) + ident(VALUE) ident(t) operator(=) ident(rb_ivar_get) operator(()ident(EmModule)operator(,) ident(Intern_at_conns)operator(\);) + ident(VALUE) ident(q) operator(=) ident(rb_hash_aref) operator(()ident(t)operator(,) ident(rb_str_new2)operator(()ident(a1)operator(\)\);) + reserved(if) operator(()ident(q) operator(==) ident(Qnil)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no connection)delimiter(")>operator(\);) + ident(rb_funcall) operator(()ident(q)operator(,) ident(Intern_notify_writable)operator(,) integer(0)operator(\);) + operator(}) + reserved(else) reserved(if) operator(()ident(a2) operator(==) ident(EM_LOOPBREAK_SIGNAL)operator(\)) operator({) + ident(rb_funcall) operator(()ident(EmModule)operator(,) ident(Intern_run_deferred_callbacks)operator(,) integer(0)operator(\);) + operator(}) + reserved(else) reserved(if) operator(()ident(a2) operator(==) ident(EM_TIMER_FIRED)operator(\)) operator({) + ident(VALUE) ident(t) operator(=) ident(rb_ivar_get) operator(()ident(EmModule)operator(,) ident(Intern_at_timers)operator(\);) + ident(VALUE) ident(q) operator(=) ident(rb_funcall) operator(()ident(t)operator(,) ident(Intern_delete)operator(,) integer(1)operator(,) ident(rb_str_new)operator(()ident(a3)operator(,) ident(a4)operator(\)\);) + reserved(if) operator(()ident(q) operator(==) ident(Qnil)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no timer)delimiter(")>operator(\);) + ident(rb_funcall) operator(()ident(q)operator(,) ident(Intern_call)operator(,) integer(0)operator(\);) + operator(}) + reserved(else) + ident(rb_funcall) operator(()ident(EmModule)operator(,) ident(Intern_event_callback)operator(,) integer(3)operator(,) ident(rb_str_new2)operator(()ident(a1)operator(\),) operator(()ident(a2) operator(<<) integer(1)operator(\)) operator(|) integer(1)operator(,) ident(rb_str_new)operator(()ident(a3)operator(,)ident(a4)operator(\)\);) +operator(}) + + + +comment(/************************** +t_initialize_event_machine +**************************/) + +directive(static) ident(VALUE) ident(t_initialize_event_machine) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(evma_initialize_library) operator(()ident(event_callback)operator(\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + + + +comment(/***************************** +t_run_machine_without_threads +*****************************/) + +directive(static) ident(VALUE) ident(t_run_machine_without_threads) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(evma_run_machine)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + + +comment(/******************* +t_add_oneshot_timer +*******************/) + +directive(static) ident(VALUE) ident(t_add_oneshot_timer) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(interval)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_install_oneshot_timer) operator(()ident(FIX2INT) operator(()ident(interval)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no timer)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + + +comment(/************** +t_start_server +**************/) + +directive(static) ident(VALUE) ident(t_start_server) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(server)operator(,) ident(VALUE) ident(port)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_create_tcp_server) operator(()ident(StringValuePtr)operator(()ident(server)operator(\),) ident(FIX2INT)operator(()ident(port)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no acceptor)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + +comment(/************* +t_stop_server +*************/) + +directive(static) ident(VALUE) ident(t_stop_server) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + ident(evma_stop_tcp_server) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + + +comment(/******************* +t_start_unix_server +*******************/) + +directive(static) ident(VALUE) ident(t_start_unix_server) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(filename)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_create_unix_domain_server) operator(()ident(StringValuePtr)operator(()ident(filename)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no unix-domain acceptor)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + + + +comment(/*********** +t_send_data +***********/) + +directive(static) ident(VALUE) ident(t_send_data) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(,) ident(VALUE) ident(data)operator(,) ident(VALUE) ident(data_length)operator(\)) +operator({) + pre_type(int) ident(b) operator(=) ident(evma_send_data_to_connection) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) ident(StringValuePtr) operator(()ident(data)operator(\),) ident(FIX2INT) operator(()ident(data_length)operator(\)\);) + reserved(return) ident(INT2NUM) operator(()ident(b)operator(\);) +operator(}) + + +comment(/*********** +t_start_tls +***********/) + +directive(static) ident(VALUE) ident(t_start_tls) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + ident(evma_start_tls) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/*************** +t_set_tls_parms +***************/) + +directive(static) ident(VALUE) ident(t_set_tls_parms) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(,) ident(VALUE) ident(privkeyfile)operator(,) ident(VALUE) ident(certchainfile)operator(\)) +operator({) + comment(/* set_tls_parms takes a series of positional arguments for specifying such things + * as private keys and certificate chains. + * It's expected that the parameter list will grow as we add more supported features. + * ALL of these parameters are optional, and can be specified as empty or NULL strings. + */) + ident(evma_set_tls_parms) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) ident(StringValuePtr) operator(()ident(privkeyfile)operator(\),) ident(StringValuePtr) operator(()ident(certchainfile)operator(\)) operator(\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/************** +t_get_peername +**************/) + +directive(static) ident(VALUE) ident(t_get_peername) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + reserved(struct) ident(sockaddr) ident(s)operator(;) + reserved(if) operator(()ident(evma_get_peername) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator(&)ident(s)operator(\)\)) operator({) + reserved(return) ident(rb_str_new) operator((()directive(const) pre_type(char)operator(*\)&)ident(s)operator(,) reserved(sizeof)operator(()ident(s)operator(\)\);) + operator(}) + + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/************** +t_get_sockname +**************/) + +directive(static) ident(VALUE) ident(t_get_sockname) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + reserved(struct) ident(sockaddr) ident(s)operator(;) + reserved(if) operator(()ident(evma_get_sockname) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator(&)ident(s)operator(\)\)) operator({) + reserved(return) ident(rb_str_new) operator((()directive(const) pre_type(char)operator(*\)&)ident(s)operator(,) reserved(sizeof)operator(()ident(s)operator(\)\);) + operator(}) + + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/******************** +t_get_subprocess_pid +********************/) + +directive(static) ident(VALUE) ident(t_get_subprocess_pid) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + ident(pid_t) ident(pid)operator(;) + reserved(if) operator(()ident(evma_get_subprocess_pid) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator(&)ident(pid)operator(\)\)) operator({) + reserved(return) ident(INT2NUM) operator(()ident(pid)operator(\);) + operator(}) + + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/*********************** +t_get_subprocess_status +***********************/) + +directive(static) ident(VALUE) ident(t_get_subprocess_status) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + pre_type(int) ident(status)operator(;) + reserved(if) operator(()ident(evma_get_subprocess_status) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator(&)ident(status)operator(\)\)) operator({) + reserved(return) ident(INT2NUM) operator(()ident(status)operator(\);) + operator(}) + + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/***************************** +t_get_comm_inactivity_timeout +*****************************/) + +directive(static) ident(VALUE) ident(t_get_comm_inactivity_timeout) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + pre_type(int) ident(timeout)operator(;) + reserved(if) operator(()ident(evma_get_comm_inactivity_timeout) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator(&)ident(timeout)operator(\)\)) + reserved(return) ident(INT2FIX) operator(()ident(timeout)operator(\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/***************************** +t_set_comm_inactivity_timeout +*****************************/) + +directive(static) ident(VALUE) ident(t_set_comm_inactivity_timeout) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(,) ident(VALUE) ident(timeout)operator(\)) +operator({) + pre_type(int) ident(ti) operator(=) ident(FIX2INT) operator(()ident(timeout)operator(\);) + reserved(if) operator(()ident(evma_set_comm_inactivity_timeout) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator(&)ident(ti)operator(\)\);) + reserved(return) ident(Qtrue)operator(;) + reserved(return) ident(Qnil)operator(;) +operator(}) + + +comment(/*************** +t_send_datagram +***************/) + +directive(static) ident(VALUE) ident(t_send_datagram) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(,) ident(VALUE) ident(data)operator(,) ident(VALUE) ident(data_length)operator(,) ident(VALUE) ident(address)operator(,) ident(VALUE) ident(port)operator(\)) +operator({) + pre_type(int) ident(b) operator(=) ident(evma_send_datagram) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) ident(StringValuePtr) operator(()ident(data)operator(\),) ident(FIX2INT) operator(()ident(data_length)operator(\),) ident(StringValuePtr)operator(()ident(address)operator(\),) ident(FIX2INT)operator(()ident(port)operator(\)\);) + reserved(return) ident(INT2NUM) operator(()ident(b)operator(\);) +operator(}) + + +comment(/****************** +t_close_connection +******************/) + +directive(static) ident(VALUE) ident(t_close_connection) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(,) ident(VALUE) ident(after_writing)operator(\)) +operator({) + ident(evma_close_connection) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\),) operator((()ident(after_writing) operator(==) ident(Qtrue)operator(\)) operator(?) integer(1) operator(:) integer(0)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/******************************** +t_report_connection_error_status +********************************/) + +directive(static) ident(VALUE) ident(t_report_connection_error_status) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + pre_type(int) ident(b) operator(=) ident(evma_report_connection_error_status) operator(()ident(StringValuePtr) operator(()ident(signature)operator(\)\);) + reserved(return) ident(INT2NUM) operator(()ident(b)operator(\);) +operator(}) + + + +comment(/**************** +t_connect_server +****************/) + +directive(static) ident(VALUE) ident(t_connect_server) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(server)operator(,) ident(VALUE) ident(port)operator(\)) +operator({) + comment(// Avoid FIX2INT in this case, because it doesn't deal with type errors properly.) + comment(// Specifically, if the value of port comes in as a string rather than an integer,) + comment(// NUM2INT will throw a type error, but FIX2INT will generate garbage.) + + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_connect_to_server) operator(()ident(StringValuePtr)operator(()ident(server)operator(\),) ident(NUM2INT)operator(()ident(port)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no connection)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + +comment(/********************* +t_connect_unix_server +*********************/) + +directive(static) ident(VALUE) ident(t_connect_unix_server) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(serversocket)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_connect_to_unix_server) operator(()ident(StringValuePtr)operator(()ident(serversocket)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no connection)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + +comment(/*********** +t_attach_fd +***********/) + +directive(static) ident(VALUE) ident(t_attach_fd) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(file_descriptor)operator(,) ident(VALUE) ident(read_mode)operator(,) ident(VALUE) ident(write_mode)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_attach_fd) operator(()ident(NUM2INT)operator(()ident(file_descriptor)operator(\),) operator(()ident(read_mode) operator(==) ident(Qtrue)operator(\)) operator(?) integer(1) operator(:) integer(0)operator(,) operator(()ident(write_mode) operator(==) ident(Qtrue)operator(\)) operator(?) integer(1) operator(:) integer(0)operator(\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no connection)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + +comment(/*********** +t_detach_fd +***********/) + +directive(static) ident(VALUE) ident(t_detach_fd) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(\)) +operator({) + reserved(return) ident(INT2NUM)operator(()ident(evma_detach_fd) operator(()ident(StringValuePtr)operator(()ident(signature)operator(\)\)\);) +operator(}) + +comment(/***************** +t_open_udp_socket +*****************/) + +directive(static) ident(VALUE) ident(t_open_udp_socket) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(server)operator(,) ident(VALUE) ident(port)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_open_datagram_socket) operator(()ident(StringValuePtr)operator(()ident(server)operator(\),) ident(FIX2INT)operator(()ident(port)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no datagram socket)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + + + +comment(/***************** +t_release_machine +*****************/) + +directive(static) ident(VALUE) ident(t_release_machine) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(evma_release_library)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + + +comment(/****** +t_stop +******/) + +directive(static) ident(VALUE) ident(t_stop) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(evma_stop_machine)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/****************** +t_signal_loopbreak +******************/) + +directive(static) ident(VALUE) ident(t_signal_loopbreak) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(evma_signal_loopbreak)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/************** +t_library_type +**************/) + +directive(static) ident(VALUE) ident(t_library_type) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + reserved(return) ident(rb_eval_string) operator(()string<delimiter(")content(:extension)delimiter(")>operator(\);) +operator(}) + + + +comment(/******************* +t_set_timer_quantum +*******************/) + +directive(static) ident(VALUE) ident(t_set_timer_quantum) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(interval)operator(\)) +operator({) + ident(evma_set_timer_quantum) operator(()ident(FIX2INT) operator(()ident(interval)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/******************** +t_set_max_timer_count +********************/) + +directive(static) ident(VALUE) ident(t_set_max_timer_count) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(ct)operator(\)) +operator({) + ident(evma_set_max_timer_count) operator(()ident(FIX2INT) operator(()ident(ct)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/*************** +t_setuid_string +***************/) + +directive(static) ident(VALUE) ident(t_setuid_string) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(username)operator(\)) +operator({) + ident(evma_setuid_string) operator(()ident(StringValuePtr) operator(()ident(username)operator(\)\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + + + +comment(/************* +t__write_file +*************/) + +directive(static) ident(VALUE) ident(t__write_file) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(filename)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma__write_file) operator(()ident(StringValuePtr) operator(()ident(filename)operator(\)\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(file not opened)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + +comment(/************** +t_invoke_popen +**************/) + +directive(static) ident(VALUE) ident(t_invoke_popen) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(cmd)operator(\)) +operator({) + comment(// 1.8.7+) + preprocessor(#ifdef) ident(RARRAY_LEN) + pre_type(int) ident(len) operator(=) ident(RARRAY_LEN)operator(()ident(cmd)operator(\);) + preprocessor(#else) + pre_type(int) ident(len) operator(=) ident(RARRAY) operator(()ident(cmd)operator(\)->)ident(len)operator(;) + preprocessor(#endif) + reserved(if) operator(()ident(len) operator(>) integer(98)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(too many arguments to popen)delimiter(")>operator(\);) + pre_type(char) operator(*)ident(strings) operator([)integer(100)operator(];) + reserved(for) operator(()pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i) operator(<) ident(len)operator(;) ident(i)operator(++\)) operator({) + ident(VALUE) ident(ix) operator(=) ident(INT2FIX) operator(()ident(i)operator(\);) + ident(VALUE) ident(s) operator(=) ident(rb_ary_aref) operator(()integer(1)operator(,) operator(&)ident(ix)operator(,) ident(cmd)operator(\);) + ident(strings)operator([)ident(i)operator(]) operator(=) ident(StringValuePtr) operator(()ident(s)operator(\);) + operator(}) + ident(strings)operator([)ident(len)operator(]) operator(=) pre_constant(NULL)operator(;) + + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_popen) operator(()ident(strings)operator(\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) operator({) + pre_type(char) operator(*)ident(err) operator(=) ident(strerror) operator(()ident(errno)operator(\);) + pre_type(char) ident(buf)operator([)integer(100)operator(];) + ident(memset) operator(()ident(buf)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)\);) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(no popen: %s)delimiter(")>operator(,) operator(()ident(err)operator(?)ident(err)operator(:)string<delimiter(")content(???)delimiter(")>operator(\)\);) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) ident(buf)operator(\);) + operator(}) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + + +comment(/*************** +t_read_keyboard +***************/) + +directive(static) ident(VALUE) ident(t_read_keyboard) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + directive(const) pre_type(char) operator(*)ident(f) operator(=) ident(evma_open_keyboard)operator((\);) + reserved(if) operator((!)ident(f) operator(||) operator(!*)ident(f)operator(\)) + ident(rb_raise) operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(no keyboard reader)delimiter(")>operator(\);) + reserved(return) ident(rb_str_new2) operator(()ident(f)operator(\);) +operator(}) + + +comment(/******** +t__epoll +********/) + +directive(static) ident(VALUE) ident(t__epoll) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + comment(// Temporary.) + ident(evma__epoll)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/********** +t__epoll_p +**********/) + +directive(static) ident(VALUE) ident(t__epoll_p) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_EPOLL) + reserved(return) ident(Qtrue)operator(;) + preprocessor(#else) + reserved(return) ident(Qfalse)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/********* +t__kqueue +*********/) + +directive(static) ident(VALUE) ident(t__kqueue) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + comment(// Temporary.) + ident(evma__kqueue)operator((\);) + reserved(return) ident(Qnil)operator(;) +operator(}) + +comment(/*********** +t__kqueue_p +***********/) + +directive(static) ident(VALUE) ident(t__kqueue_p) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + preprocessor(#ifdef) ident(HAVE_KQUEUE) + reserved(return) ident(Qtrue)operator(;) + preprocessor(#else) + reserved(return) ident(Qfalse)operator(;) + preprocessor(#endif) +operator(}) + + +comment(/**************** +t_send_file_data +****************/) + +directive(static) ident(VALUE) ident(t_send_file_data) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(signature)operator(,) ident(VALUE) ident(filename)operator(\)) +operator({) + + comment(/* The current implementation of evma_send_file_data_to_connection enforces a strict + * upper limit on the file size it will transmit (currently 32K\). The function returns + * zero on success, -1 if the requested file exceeds its size limit, and a positive + * number for other errors. + * TODO: Positive return values are actually errno's, which is probably the wrong way to + * do this. For one thing it's ugly. For another, we can't be sure zero is never a real errno. + */) + + pre_type(int) ident(b) operator(=) ident(evma_send_file_data_to_connection) operator(()ident(StringValuePtr)operator(()ident(signature)operator(\),) ident(StringValuePtr)operator(()ident(filename)operator(\)\);) + reserved(if) operator(()ident(b) operator(==) operator(-)integer(1)operator(\)) + ident(rb_raise)operator(()ident(rb_eRuntimeError)operator(,) string<delimiter(")content(File too large. send_file_data(\) supports files under 32k.)delimiter(")>operator(\);) + reserved(if) operator(()ident(b) operator(>) integer(0)operator(\)) operator({) + pre_type(char) operator(*)ident(err) operator(=) ident(strerror) operator(()ident(b)operator(\);) + pre_type(char) ident(buf)operator([)integer(1024)operator(];) + ident(memset) operator(()ident(buf)operator(,) integer(0)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)\);) + ident(snprintf) operator(()ident(buf)operator(,) reserved(sizeof)operator(()ident(buf)operator(\)-)integer(1)operator(,) string<delimiter(")content(: %s %s)delimiter(")>operator(,) ident(StringValuePtr)operator(()ident(filename)operator(\),()ident(err)operator(?)ident(err)operator(:)string<delimiter(")content(???)delimiter(")>operator(\)\);) + + ident(rb_raise) operator(()ident(rb_eIOError)operator(,) ident(buf)operator(\);) + operator(}) + + reserved(return) ident(INT2NUM) operator(()integer(0)operator(\);) +operator(}) + + +comment(/******************* +t_set_rlimit_nofile +*******************/) + +directive(static) ident(VALUE) ident(t_set_rlimit_nofile) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(arg)operator(\)) +operator({) + ident(arg) operator(=) operator(()ident(NIL_P)operator(()ident(arg)operator(\)\)) operator(?) operator(-)integer(1) operator(:) ident(NUM2INT) operator(()ident(arg)operator(\);) + reserved(return) ident(INT2NUM) operator(()ident(evma_set_rlimit_nofile) operator(()ident(arg)operator(\)\);) +operator(}) + +comment(/*************************** +conn_get_outbound_data_size +***************************/) + +directive(static) ident(VALUE) ident(conn_get_outbound_data_size) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(VALUE) ident(sig) operator(=) ident(rb_ivar_get) operator(()ident(self)operator(,) ident(Intern_at_signature)operator(\);) + reserved(return) ident(INT2NUM) operator(()ident(evma_get_outbound_data_size) operator(()ident(StringValuePtr)operator(()ident(sig)operator(\)\)\);) +operator(}) + + +comment(/****************************** +conn_associate_callback_target +******************************/) + +directive(static) ident(VALUE) ident(conn_associate_callback_target) operator(()ident(VALUE) ident(self)operator(,) ident(VALUE) ident(sig)operator(\)) +operator({) + comment(// No-op for the time being.) + reserved(return) ident(Qnil)operator(;) +operator(}) + + +comment(/*************** +t_get_loop_time +****************/) + +directive(static) ident(VALUE) ident(t_get_loop_time) operator(()ident(VALUE) ident(self)operator(\)) +operator({) + ident(VALUE) ident(cTime) operator(=) ident(rb_path2class)operator(()string<delimiter(")content(Time)delimiter(")>operator(\);) + reserved(if) operator(()ident(gCurrentLoopTime) operator(!=) integer(0)operator(\)) operator({) + reserved(return) ident(rb_funcall)operator(()ident(cTime)operator(,) + ident(rb_intern)operator(()string<delimiter(")content(at)delimiter(")>operator(\),) + integer(1)operator(,) + ident(INT2NUM)operator(()ident(gCurrentLoopTime)operator(\)\);) + operator(}) + reserved(return) ident(Qnil)operator(;) +operator(}) + + +comment(/********************* +Init_rubyeventmachine +*********************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> directive(void) ident(Init_rubyeventmachine)operator((\)) +operator({) + comment(// Tuck away some symbol values so we don't have to look 'em up every time we need 'em.) + ident(Intern_at_signature) operator(=) ident(rb_intern) operator(()string<delimiter(")content(@signature)delimiter(")>operator(\);) + ident(Intern_at_timers) operator(=) ident(rb_intern) operator(()string<delimiter(")content(@timers)delimiter(")>operator(\);) + ident(Intern_at_conns) operator(=) ident(rb_intern) operator(()string<delimiter(")content(@conns)delimiter(")>operator(\);) + + ident(Intern_event_callback) operator(=) ident(rb_intern) operator(()string<delimiter(")content(event_callback)delimiter(")>operator(\);) + ident(Intern_run_deferred_callbacks) operator(=) ident(rb_intern) operator(()string<delimiter(")content(run_deferred_callbacks)delimiter(")>operator(\);) + ident(Intern_delete) operator(=) ident(rb_intern) operator(()string<delimiter(")content(delete)delimiter(")>operator(\);) + ident(Intern_call) operator(=) ident(rb_intern) operator(()string<delimiter(")content(call)delimiter(")>operator(\);) + ident(Intern_receive_data) operator(=) ident(rb_intern) operator(()string<delimiter(")content(receive_data)delimiter(")>operator(\);) + + ident(Intern_notify_readable) operator(=) ident(rb_intern) operator(()string<delimiter(")content(notify_readable)delimiter(")>operator(\);) + ident(Intern_notify_writable) operator(=) ident(rb_intern) operator(()string<delimiter(")content(notify_writable)delimiter(")>operator(\);) + + comment(// INCOMPLETE, we need to define class Connections inside module EventMachine) + comment(// run_machine and run_machine_without_threads are now identical.) + comment(// Must deprecate the without_threads variant.) + ident(EmModule) operator(=) ident(rb_define_module) operator(()string<delimiter(")content(EventMachine)delimiter(")>operator(\);) + ident(EmConnection) operator(=) ident(rb_define_class_under) operator(()ident(EmModule)operator(,) string<delimiter(")content(Connection)delimiter(")>operator(,) ident(rb_cObject)operator(\);) + + ident(rb_define_class_under) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionNotBound)delimiter(")>operator(,) ident(rb_eException)operator(\);) + ident(rb_define_class_under) operator(()ident(EmModule)operator(,) string<delimiter(")content(NoHandlerForAcceptedConnection)delimiter(")>operator(,) ident(rb_eException)operator(\);) + ident(rb_define_class_under) operator(()ident(EmModule)operator(,) string<delimiter(")content(UnknownTimerFired)delimiter(")>operator(,) ident(rb_eException)operator(\);) + + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(initialize_event_machine)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_initialize_event_machine)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(run_machine)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_run_machine_without_threads)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(run_machine_without_threads)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_run_machine_without_threads)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(add_oneshot_timer)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_add_oneshot_timer)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(start_tcp_server)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_start_server)operator(,) integer(2)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(stop_tcp_server)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_stop_server)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(start_unix_server)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_start_unix_server)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(set_tls_parms)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_set_tls_parms)operator(,) integer(3)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(start_tls)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_start_tls)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(send_data)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_send_data)operator(,) integer(3)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(send_datagram)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_send_datagram)operator(,) integer(5)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(close_connection)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_close_connection)operator(,) integer(2)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(report_connection_error_status)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_report_connection_error_status)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(connect_server)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_connect_server)operator(,) integer(2)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(connect_unix_server)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_connect_unix_server)operator(,) integer(1)operator(\);) + + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(attach_fd)delimiter(")>operator(,) operator(()ident(VALUE) operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_attach_fd)operator(,) integer(3)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(detach_fd)delimiter(")>operator(,) operator(()ident(VALUE) operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_detach_fd)operator(,) integer(1)operator(\);) + + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(current_time)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_get_loop_time)operator(,) integer(0)operator(\);) + + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(open_udp_socket)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_open_udp_socket)operator(,) integer(2)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(read_keyboard)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_read_keyboard)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(release_machine)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_release_machine)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(stop)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_stop)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(signal_loopbreak)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_signal_loopbreak)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(library_type)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_library_type)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(set_timer_quantum)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_set_timer_quantum)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(set_max_timer_count)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_set_max_timer_count)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(setuid_string)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_setuid_string)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(invoke_popen)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_invoke_popen)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(send_file_data)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_send_file_data)operator(,) integer(2)operator(\);) + + comment(// Provisional:) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(_write_file)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t__write_file)operator(,) integer(1)operator(\);) + + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(get_peername)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_get_peername)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(get_sockname)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_get_sockname)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(get_subprocess_pid)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_get_subprocess_pid)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(get_subprocess_status)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_get_subprocess_status)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(get_comm_inactivity_timeout)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_get_comm_inactivity_timeout)operator(,) integer(1)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(set_comm_inactivity_timeout)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_set_comm_inactivity_timeout)operator(,) integer(2)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(set_rlimit_nofile)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t_set_rlimit_nofile)operator(,) integer(1)operator(\);) + + comment(// Temporary:) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(epoll)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t__epoll)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(kqueue)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t__kqueue)operator(,) integer(0)operator(\);) + + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(epoll?)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t__epoll_p)operator(,) integer(0)operator(\);) + ident(rb_define_module_function) operator(()ident(EmModule)operator(,) string<delimiter(")content(kqueue?)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(t__kqueue_p)operator(,) integer(0)operator(\);) + + ident(rb_define_method) operator(()ident(EmConnection)operator(,) string<delimiter(")content(get_outbound_data_size)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(conn_get_outbound_data_size)operator(,) integer(0)operator(\);) + ident(rb_define_method) operator(()ident(EmConnection)operator(,) string<delimiter(")content(associate_callback_target)delimiter(")>operator(,) operator(()ident(VALUE)operator((*\)()operator(.)operator(.)operator(.)operator(\)\))ident(conn_associate_callback_target)operator(,) integer(1)operator(\);) + + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(TimerFired)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(100)operator(\)\);) + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionData)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(101)operator(\)\);) + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionUnbound)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(102)operator(\)\);) + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionAccepted)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(103)operator(\)\);) + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionCompleted)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(104)operator(\)\);) + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(LoopbreakSignalled)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(105)operator(\)\);) + + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionNotifyReadable)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(106)operator(\)\);) + ident(rb_define_const) operator(()ident(EmModule)operator(,) string<delimiter(")content(ConnectionNotifyWritable)delimiter(")>operator(,) ident(INT2NUM)operator(()integer(107)operator(\)\);) + +operator(}) + +comment(/***************************************************************************** + +$Id$ + +File: sigs.cpp +Date: 06Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + +preprocessor(#include) include("project.h") + + +pre_type(bool) ident(gTerminateSignalReceived)operator(;) + + +comment(/************** +SigtermHandler +**************/) + +directive(void) ident(SigtermHandler) operator(()pre_type(int) ident(sig)operator(\)) +operator({) + comment(// This is a signal-handler, don't do anything frisky. Interrupts are disabled.) + comment(// Set the terminate flag WITHOUT trying to lock a mutex- otherwise we can easily) + comment(// self-deadlock, especially if the event machine is looping quickly.) + ident(gTerminateSignalReceived) operator(=) pre_constant(true)operator(;) +operator(}) + + +comment(/********************* +InstallSignalHandlers +*********************/) + +directive(void) ident(InstallSignalHandlers)operator((\)) +operator({) + preprocessor(#ifdef) ident(OS_UNIX) + directive(static) pre_type(bool) ident(bInstalled) operator(=) pre_constant(false)operator(;) + reserved(if) operator((!)ident(bInstalled)operator(\)) operator({) + ident(bInstalled) operator(=) pre_constant(true)operator(;) + ident(signal) operator(()ident(SIGINT)operator(,) ident(SigtermHandler)operator(\);) + ident(signal) operator(()ident(SIGTERM)operator(,) ident(SigtermHandler)operator(\);) + ident(signal) operator(()ident(SIGPIPE)operator(,) ident(SIG_IGN)operator(\);) + operator(}) + preprocessor(#endif) +operator(}) + + + +comment(/******************* +WintelSignalHandler +*******************/) + +preprocessor(#ifdef) ident(OS_WIN32) +ident(BOOL) ident(WINAPI) ident(WintelSignalHandler) operator(()ident(DWORD) ident(control)operator(\)) +operator({) + reserved(if) operator(()ident(control) operator(==) ident(CTRL_C_EVENT)operator(\)) + ident(gTerminateSignalReceived) operator(=) pre_constant(true)operator(;) + reserved(return) ident(TRUE)operator(;) +operator(}) +preprocessor(#endif) + +comment(/************ +HookControlC +************/) + +preprocessor(#ifdef) ident(OS_WIN32) +directive(void) ident(HookControlC) operator(()pre_type(bool) ident(hook)operator(\)) +operator({) + reserved(if) operator(()ident(hook)operator(\)) operator({) + comment(// INSTALL hook) + ident(SetConsoleCtrlHandler) operator(()ident(WintelSignalHandler)operator(,) ident(TRUE)operator(\);) + operator(}) + reserved(else) operator({) + comment(// UNINSTALL hook) + ident(SetConsoleCtrlHandler) operator(()ident(WintelSignalHandler)operator(,) ident(FALSE)operator(\);) + operator(}) +operator(}) +preprocessor(#endif) + + +comment(/***************************************************************************** + +$Id$ + +File: ssl.cpp +Date: 30Apr06 + +Copyright (C\) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1\) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option\) any later version; or 2\) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/) + + +preprocessor(#ifdef) ident(WITH_SSL) + +preprocessor(#include) include("project.h") + + +pre_type(bool) ident(SslContext_t)operator(::)ident(bLibraryInitialized) operator(=) pre_constant(false)operator(;) + + + +directive(static) directive(void) ident(InitializeDefaultCredentials)operator((\);) +directive(static) ident(EVP_PKEY) operator(*)ident(DefaultPrivateKey) operator(=) pre_constant(NULL)operator(;) +directive(static) ident(X509) operator(*)ident(DefaultCertificate) operator(=) pre_constant(NULL)operator(;) + +directive(static) pre_type(char) ident(PrivateMaterials)operator([]) operator(=) operator({) +string<delimiter(")content(-----BEGIN RSA PRIVATE KEY-----)char(\\n)delimiter(")> +string<delimiter(")content(MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV)char(\\n)delimiter(")> +string<delimiter(")content(Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/)char(\\n)delimiter(")> +string<delimiter(")content(AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB)char(\\n)delimiter(")> +string<delimiter(")content(AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk)char(\\n)delimiter(")> +string<delimiter(")content(H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D)char(\\n)delimiter(")> +string<delimiter(")content(I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo)char(\\n)delimiter(")> +string<delimiter(")content(6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg)char(\\n)delimiter(")> +string<delimiter(")content(w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK)char(\\n)delimiter(")> +string<delimiter(")content(PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ)char(\\n)delimiter(")> +string<delimiter(")content(xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k)char(\\n)delimiter(")> +string<delimiter(")content(xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa)char(\\n)delimiter(")> +string<delimiter(")content(dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn)char(\\n)delimiter(")> +string<delimiter(")content(2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=)char(\\n)delimiter(")> +string<delimiter(")content(-----END RSA PRIVATE KEY-----)char(\\n)delimiter(")> +string<delimiter(")content(-----BEGIN CERTIFICATE-----)char(\\n)delimiter(")> +string<delimiter(")content(MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD)char(\\n)delimiter(")> +string<delimiter(")content(VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw)char(\\n)delimiter(")> +string<delimiter(")content(FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG)char(\\n)delimiter(")> +string<delimiter(")content(A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu)char(\\n)delimiter(")> +string<delimiter(")content(ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw)char(\\n)delimiter(")> +string<delimiter(")content(NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH)char(\\n)delimiter(")> +string<delimiter(")content(EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n)char(\\n)delimiter(")> +string<delimiter(")content(aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI)char(\\n)delimiter(")> +string<delimiter(")content(hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB)char(\\n)delimiter(")> +string<delimiter(")content(AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw)char(\\n)delimiter(")> +string<delimiter(")content(VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3)char(\\n)delimiter(")> +string<delimiter(")content(9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID)char(\\n)delimiter(")> +string<delimiter(")content(AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV)char(\\n)delimiter(")> +string<delimiter(")content(HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG)char(\\n)delimiter(")> +string<delimiter(")content(EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD)char(\\n)delimiter(")> +string<delimiter(")content(VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE)char(\\n)delimiter(")> +string<delimiter(")content(AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy)char(\\n)delimiter(")> +string<delimiter(")content(aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG)char(\\n)delimiter(")> +string<delimiter(")content(SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0)char(\\n)delimiter(")> +string<delimiter(")content(Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j)char(\\n)delimiter(")> +string<delimiter(")content(uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy)char(\\n)delimiter(")> +string<delimiter(")content(-----END CERTIFICATE-----)char(\\n)delimiter(")>operator(};) + +comment(/* These private materials were made with: + * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500 + * TODO: We need a full-blown capability to work with user-supplied + * keypairs and properly-signed certificates. + */) + + +comment(/***************** +builtin_passwd_cb +*****************/) + +directive(extern) string<delimiter(")content(C)delimiter(")> pre_type(int) ident(builtin_passwd_cb) operator(()pre_type(char) operator(*)ident(buf)operator(,) pre_type(int) ident(bufsize)operator(,) pre_type(int) ident(rwflag)operator(,) directive(void) operator(*)ident(userdata)operator(\)) +operator({) + ident(strcpy) operator(()ident(buf)operator(,) string<delimiter(")content(kittycat)delimiter(")>operator(\);) + reserved(return) integer(8)operator(;) +operator(}) + +comment(/**************************** +InitializeDefaultCredentials +****************************/) + +directive(static) directive(void) ident(InitializeDefaultCredentials)operator((\)) +operator({) + ident(BIO) operator(*)ident(bio) operator(=) ident(BIO_new_mem_buf) operator(()ident(PrivateMaterials)operator(,) operator(-)integer(1)operator(\);) + ident(assert) operator(()ident(bio)operator(\);) + + reserved(if) operator(()ident(DefaultPrivateKey)operator(\)) operator({) + comment(// we may come here in a restart.) + ident(EVP_PKEY_free) operator(()ident(DefaultPrivateKey)operator(\);) + ident(DefaultPrivateKey) operator(=) pre_constant(NULL)operator(;) + operator(}) + ident(PEM_read_bio_PrivateKey) operator(()ident(bio)operator(,) operator(&)ident(DefaultPrivateKey)operator(,) ident(builtin_passwd_cb)operator(,) integer(0)operator(\);) + + reserved(if) operator(()ident(DefaultCertificate)operator(\)) operator({) + comment(// we may come here in a restart.) + ident(X509_free) operator(()ident(DefaultCertificate)operator(\);) + ident(DefaultCertificate) operator(=) pre_constant(NULL)operator(;) + operator(}) + ident(PEM_read_bio_X509) operator(()ident(bio)operator(,) operator(&)ident(DefaultCertificate)operator(,) pre_constant(NULL)operator(,) integer(0)operator(\);) + + ident(BIO_free) operator(()ident(bio)operator(\);) +operator(}) + + + +comment(/************************** +SslContext_t::SslContext_t +**************************/) + +ident(SslContext_t)operator(::)ident(SslContext_t) operator(()pre_type(bool) ident(is_server)operator(,) directive(const) pre_type(string) operator(&)ident(privkeyfile)operator(,) directive(const) pre_type(string) operator(&)ident(certchainfile)operator(\):) + ident(pCtx) operator(()pre_constant(NULL)operator(\),) + ident(PrivateKey) operator(()pre_constant(NULL)operator(\),) + ident(Certificate) operator(()pre_constant(NULL)operator(\)) +operator({) + comment(/* TODO: the usage of the specified private-key and cert-chain filenames only applies to + * client-side connections at this point. Server connections currently use the default materials. + * That needs to be fixed asap. + * Also, in this implementation, server-side connections use statically defined X-509 defaults. + * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY + * objects when we call our destructor, or whether just calling SSL_CTX_free is enough. + */) + + reserved(if) operator((!)ident(bLibraryInitialized)operator(\)) operator({) + ident(bLibraryInitialized) operator(=) pre_constant(true)operator(;) + ident(SSL_library_init)operator((\);) + ident(OpenSSL_add_ssl_algorithms)operator((\);) + ident(OpenSSL_add_all_algorithms)operator((\);) + ident(SSL_load_error_strings)operator((\);) + ident(ERR_load_crypto_strings)operator((\);) + + ident(InitializeDefaultCredentials)operator((\);) + operator(}) + + ident(bIsServer) operator(=) ident(is_server)operator(;) + ident(pCtx) operator(=) ident(SSL_CTX_new) operator(()ident(is_server) operator(?) ident(SSLv23_server_method)operator((\)) operator(:) ident(SSLv23_client_method)operator((\)\);) + reserved(if) operator((!)ident(pCtx)operator(\)) + reserved(throw) ident(std)operator(::)ident(runtime_error) operator(()string<delimiter(")content(no SSL context)delimiter(")>operator(\);) + + ident(SSL_CTX_set_options) operator(()ident(pCtx)operator(,) ident(SSL_OP_ALL)operator(\);) + comment(//SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3\)\);) + + reserved(if) operator(()ident(is_server)operator(\)) operator({) + comment(// The SSL_CTX calls here do NOT allocate memory.) + pre_type(int) ident(e)operator(;) + reserved(if) operator(()ident(privkeyfile)operator(.)ident(length)operator((\)) operator(>) integer(0)operator(\)) + ident(e) operator(=) ident(SSL_CTX_use_PrivateKey_file) operator(()ident(pCtx)operator(,) ident(privkeyfile)operator(.)ident(c_str)operator((\),) ident(SSL_FILETYPE_PEM)operator(\);) + reserved(else) + ident(e) operator(=) ident(SSL_CTX_use_PrivateKey) operator(()ident(pCtx)operator(,) ident(DefaultPrivateKey)operator(\);) + ident(assert) operator(()ident(e) operator(>) integer(0)operator(\);) + reserved(if) operator(()ident(certchainfile)operator(.)ident(length)operator((\)) operator(>) integer(0)operator(\)) + ident(e) operator(=) ident(SSL_CTX_use_certificate_chain_file) operator(()ident(pCtx)operator(,) ident(certchainfile)operator(.)ident(c_str)operator((\)\);) + reserved(else) + ident(e) operator(=) ident(SSL_CTX_use_certificate) operator(()ident(pCtx)operator(,) ident(DefaultCertificate)operator(\);) + ident(assert) operator(()ident(e) operator(>) integer(0)operator(\);) + operator(}) + + ident(SSL_CTX_set_cipher_list) operator(()ident(pCtx)operator(,) string<delimiter(")content(ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH)delimiter(")>operator(\);) + + reserved(if) operator(()ident(is_server)operator(\)) operator({) + ident(SSL_CTX_sess_set_cache_size) operator(()ident(pCtx)operator(,) integer(128)operator(\);) + ident(SSL_CTX_set_session_id_context) operator(()ident(pCtx)operator(,) operator(()pre_type(unsigned) pre_type(char)operator(*\))string<delimiter(")content(eventmachine)delimiter(")>operator(,) integer(12)operator(\);) + operator(}) + reserved(else) operator({) + pre_type(int) ident(e)operator(;) + reserved(if) operator(()ident(privkeyfile)operator(.)ident(length)operator((\)) operator(>) integer(0)operator(\)) operator({) + ident(e) operator(=) ident(SSL_CTX_use_PrivateKey_file) operator(()ident(pCtx)operator(,) ident(privkeyfile)operator(.)ident(c_str)operator((\),) ident(SSL_FILETYPE_PEM)operator(\);) + ident(assert) operator(()ident(e) operator(>) integer(0)operator(\);) + operator(}) + reserved(if) operator(()ident(certchainfile)operator(.)ident(length)operator((\)) operator(>) integer(0)operator(\)) operator({) + ident(e) operator(=) ident(SSL_CTX_use_certificate_chain_file) operator(()ident(pCtx)operator(,) ident(certchainfile)operator(.)ident(c_str)operator((\)\);) + ident(assert) operator(()ident(e) operator(>) integer(0)operator(\);) + operator(}) + operator(}) +operator(}) + + + +comment(/*************************** +SslContext_t::~SslContext_t +***************************/) + +ident(SslContext_t)operator(::~)ident(SslContext_t)operator((\)) +operator({) + reserved(if) operator(()ident(pCtx)operator(\)) + ident(SSL_CTX_free) operator(()ident(pCtx)operator(\);) + reserved(if) operator(()ident(PrivateKey)operator(\)) + ident(EVP_PKEY_free) operator(()ident(PrivateKey)operator(\);) + reserved(if) operator(()ident(Certificate)operator(\)) + ident(X509_free) operator(()ident(Certificate)operator(\);) +operator(}) + + + +comment(/****************** +SslBox_t::SslBox_t +******************/) + +ident(SslBox_t)operator(::)ident(SslBox_t) operator(()pre_type(bool) ident(is_server)operator(,) directive(const) pre_type(string) operator(&)ident(privkeyfile)operator(,) directive(const) pre_type(string) operator(&)ident(certchainfile)operator(\):) + ident(bIsServer) operator(()ident(is_server)operator(\),) + ident(pSSL) operator(()pre_constant(NULL)operator(\),) + ident(pbioRead) operator(()pre_constant(NULL)operator(\),) + ident(pbioWrite) operator(()pre_constant(NULL)operator(\)) +operator({) + comment(/* TODO someday: make it possible to re-use SSL contexts so we don't have to create + * a new one every time we come here. + */) + + ident(Context) operator(=) reserved(new) ident(SslContext_t) operator(()ident(bIsServer)operator(,) ident(privkeyfile)operator(,) ident(certchainfile)operator(\);) + ident(assert) operator(()ident(Context)operator(\);) + + ident(pbioRead) operator(=) ident(BIO_new) operator(()ident(BIO_s_mem)operator((\)\);) + ident(assert) operator(()ident(pbioRead)operator(\);) + + ident(pbioWrite) operator(=) ident(BIO_new) operator(()ident(BIO_s_mem)operator((\)\);) + ident(assert) operator(()ident(pbioWrite)operator(\);) + + ident(pSSL) operator(=) ident(SSL_new) operator(()ident(Context)operator(->)ident(pCtx)operator(\);) + ident(assert) operator(()ident(pSSL)operator(\);) + ident(SSL_set_bio) operator(()ident(pSSL)operator(,) ident(pbioRead)operator(,) ident(pbioWrite)operator(\);) + + reserved(if) operator((!)ident(bIsServer)operator(\)) + ident(SSL_connect) operator(()ident(pSSL)operator(\);) +operator(}) + + + +comment(/******************* +SslBox_t::~SslBox_t +*******************/) + +ident(SslBox_t)operator(::~)ident(SslBox_t)operator((\)) +operator({) + comment(// Freeing pSSL will also free the associated BIOs, so DON'T free them separately.) + reserved(if) operator(()ident(pSSL)operator(\)) operator({) + reserved(if) operator(()ident(SSL_get_shutdown) operator(()ident(pSSL)operator(\)) operator(&) ident(SSL_RECEIVED_SHUTDOWN)operator(\)) + ident(SSL_shutdown) operator(()ident(pSSL)operator(\);) + reserved(else) + ident(SSL_clear) operator(()ident(pSSL)operator(\);) + ident(SSL_free) operator(()ident(pSSL)operator(\);) + operator(}) + + reserved(delete) ident(Context)operator(;) +operator(}) + + + +comment(/*********************** +SslBox_t::PutCiphertext +***********************/) + +pre_type(bool) ident(SslBox_t)operator(::)ident(PutCiphertext) operator(()directive(const) pre_type(char) operator(*)ident(buf)operator(,) pre_type(int) ident(bufsize)operator(\)) +operator({) + ident(assert) operator(()ident(buf) operator(&&) operator(()ident(bufsize) operator(>) integer(0)operator(\)\);) + + ident(assert) operator(()ident(pbioRead)operator(\);) + pre_type(int) ident(n) operator(=) ident(BIO_write) operator(()ident(pbioRead)operator(,) ident(buf)operator(,) ident(bufsize)operator(\);) + + reserved(return) operator(()ident(n) operator(==) ident(bufsize)operator(\)) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(;) +operator(}) + + +comment(/********************** +SslBox_t::GetPlaintext +**********************/) + +pre_type(int) ident(SslBox_t)operator(::)ident(GetPlaintext) operator(()pre_type(char) operator(*)ident(buf)operator(,) pre_type(int) ident(bufsize)operator(\)) +operator({) + reserved(if) operator((!)ident(SSL_is_init_finished) operator(()ident(pSSL)operator(\)\)) operator({) + pre_type(int) ident(e) operator(=) ident(bIsServer) operator(?) ident(SSL_accept) operator(()ident(pSSL)operator(\)) operator(:) ident(SSL_connect) operator(()ident(pSSL)operator(\);) + reserved(if) operator(()ident(e) operator(<) integer(0)operator(\)) operator({) + pre_type(int) ident(er) operator(=) ident(SSL_get_error) operator(()ident(pSSL)operator(,) ident(e)operator(\);) + reserved(if) operator(()ident(er) operator(!=) ident(SSL_ERROR_WANT_READ)operator(\)) operator({) + comment(// Return -1 for a nonfatal error, -2 for an error that should force the connection down.) + reserved(return) operator(()ident(er) operator(==) ident(SSL_ERROR_SSL)operator(\)) operator(?) operator((-)integer(2)operator(\)) operator(:) operator((-)integer(1)operator(\);) + operator(}) + reserved(else) + reserved(return) integer(0)operator(;) + operator(}) + comment(// If handshake finished, FALL THROUGH and return the available plaintext.) + operator(}) + + reserved(if) operator((!)ident(SSL_is_init_finished) operator(()ident(pSSL)operator(\)\)) operator({) + comment(// We can get here if a browser abandons a handshake.) + comment(// The user can see a warning dialog and abort the connection.) + ident(cerr) operator(<<) string<delimiter(")content(<SSL_incomp>)delimiter(")>operator(;) + reserved(return) integer(0)operator(;) + operator(}) + + comment(//cerr << "CIPH: " << SSL_get_cipher (pSSL\) << endl;) + + pre_type(int) ident(n) operator(=) ident(SSL_read) operator(()ident(pSSL)operator(,) ident(buf)operator(,) ident(bufsize)operator(\);) + reserved(if) operator(()ident(n) operator(>=) integer(0)operator(\)) operator({) + reserved(return) ident(n)operator(;) + operator(}) + reserved(else) operator({) + reserved(if) operator(()ident(SSL_get_error) operator(()ident(pSSL)operator(,) ident(n)operator(\)) operator(==) ident(SSL_ERROR_WANT_READ)operator(\)) operator({) + reserved(return) integer(0)operator(;) + operator(}) + reserved(else) operator({) + reserved(return) operator(-)integer(1)operator(;) + operator(}) + operator(}) + + reserved(return) integer(0)operator(;) +operator(}) + + + +comment(/************************** +SslBox_t::CanGetCiphertext +**************************/) + +pre_type(bool) ident(SslBox_t)operator(::)ident(CanGetCiphertext)operator((\)) +operator({) + ident(assert) operator(()ident(pbioWrite)operator(\);) + reserved(return) ident(BIO_pending) operator(()ident(pbioWrite)operator(\)) operator(?) pre_constant(true) operator(:) pre_constant(false)operator(;) +operator(}) + + + +comment(/*********************** +SslBox_t::GetCiphertext +***********************/) + +pre_type(int) ident(SslBox_t)operator(::)ident(GetCiphertext) operator(()pre_type(char) operator(*)ident(buf)operator(,) pre_type(int) ident(bufsize)operator(\)) +operator({) + ident(assert) operator(()ident(pbioWrite)operator(\);) + ident(assert) operator(()ident(buf) operator(&&) operator(()ident(bufsize) operator(>) integer(0)operator(\)\);) + + reserved(return) ident(BIO_read) operator(()ident(pbioWrite)operator(,) ident(buf)operator(,) ident(bufsize)operator(\);) +operator(}) + + + +comment(/********************** +SslBox_t::PutPlaintext +**********************/) + +pre_type(int) ident(SslBox_t)operator(::)ident(PutPlaintext) operator(()directive(const) pre_type(char) operator(*)ident(buf)operator(,) pre_type(int) ident(bufsize)operator(\)) +operator({) + comment(// The caller will interpret the return value as the number of bytes written.) + comment(// WARNING WARNING WARNING, are there any situations in which a 0 or -1 return) + comment(// from SSL_write means we should immediately retry? The socket-machine loop) + comment(// will probably wait for a time-out cycle (perhaps a second\) before re-trying.) + comment(// THIS WOULD CAUSE A PERCEPTIBLE DELAY!) + + comment(/* We internally queue any outbound plaintext that can't be dispatched + * because we're in the middle of a handshake or something. + * When we get called, try to send any queued data first, and then + * send the caller's data (or queue it\). We may get called with no outbound + * data, which means we try to send the outbound queue and that's all. + * + * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error. + * Note that if we return 0, the connection is still considered live + * and we are signalling that we have accepted the outbound data (if any\). + */) + + ident(OutboundQ)operator(.)ident(Push) operator(()ident(buf)operator(,) ident(bufsize)operator(\);) + + reserved(if) operator((!)ident(SSL_is_init_finished) operator(()ident(pSSL)operator(\)\)) + reserved(return) integer(0)operator(;) + + pre_type(bool) ident(fatal) operator(=) pre_constant(false)operator(;) + pre_type(bool) ident(did_work) operator(=) pre_constant(false)operator(;) + + reserved(while) operator(()ident(OutboundQ)operator(.)ident(HasPages)operator((\)\)) operator({) + directive(const) pre_type(char) operator(*)ident(page)operator(;) + pre_type(int) ident(length)operator(;) + ident(OutboundQ)operator(.)ident(Front) operator((&)ident(page)operator(,) operator(&)ident(length)operator(\);) + ident(assert) operator(()ident(page) operator(&&) operator(()ident(length) operator(>) integer(0)operator(\)\);) + pre_type(int) ident(n) operator(=) ident(SSL_write) operator(()ident(pSSL)operator(,) ident(page)operator(,) ident(length)operator(\);) + reserved(if) operator(()ident(n) operator(>) integer(0)operator(\)) operator({) + ident(did_work) operator(=) pre_constant(true)operator(;) + ident(OutboundQ)operator(.)ident(PopFront)operator((\);) + operator(}) + reserved(else) operator({) + pre_type(int) ident(er) operator(=) ident(SSL_get_error) operator(()ident(pSSL)operator(,) ident(n)operator(\);) + reserved(if) operator((()ident(er) operator(!=) ident(SSL_ERROR_WANT_READ)operator(\)) operator(&&) operator(()ident(er) operator(!=) ident(SSL_ERROR_WANT_WRITE)operator(\)\)) + ident(fatal) operator(=) pre_constant(true)operator(;) + reserved(break)operator(;) + operator(}) + operator(}) + + + reserved(if) operator(()ident(did_work)operator(\)) + reserved(return) integer(1)operator(;) + reserved(else) reserved(if) operator(()ident(fatal)operator(\)) + reserved(return) operator(-)integer(1)operator(;) + reserved(else) + reserved(return) integer(0)operator(;) +operator(}) + + +preprocessor(#endif) comment(// WITH_SSL) + diff --git a/test/scanners/cpp/eventmachine.in.cpp b/test/scanners/cpp/eventmachine.in.cpp new file mode 100644 index 0000000..050d601 --- /dev/null +++ b/test/scanners/cpp/eventmachine.in.cpp @@ -0,0 +1,7035 @@ +/***************************************************************************** + +$Id$ + +File: binder.cpp +Date: 07Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + +#define DEV_URANDOM "/dev/urandom" + + +map<string, Bindable_t*> Bindable_t::BindingBag; + + +/******************************** +STATIC Bindable_t::CreateBinding +********************************/ + +string Bindable_t::CreateBinding() +{ + static int index = 0; + static string seed; + + if ((index >= 1000000) || (seed.length() == 0)) { + #ifdef OS_UNIX + int fd = open (DEV_URANDOM, O_RDONLY); + if (fd < 0) + throw std::runtime_error ("No entropy device"); + + unsigned char u[16]; + size_t r = read (fd, u, sizeof(u)); + if (r < sizeof(u)) + throw std::runtime_error ("Unable to read entropy device"); + + unsigned char *u1 = (unsigned char*)u; + char u2 [sizeof(u) * 2 + 1]; + + for (size_t i=0; i < sizeof(u); i++) + sprintf (u2 + (i * 2), "%02x", u1[i]); + + seed = string (u2); + #endif + + + #ifdef OS_WIN32 + UUID uuid; + UuidCreate (&uuid); + unsigned char *uuidstring = NULL; + UuidToString (&uuid, &uuidstring); + if (!uuidstring) + throw std::runtime_error ("Unable to read uuid"); + seed = string ((const char*)uuidstring); + + RpcStringFree (&uuidstring); + #endif + + index = 0; + + + } + + stringstream ss; + ss << seed << (++index); + return ss.str(); +} + + +/***************************** +STATIC: Bindable_t::GetObject +*****************************/ + +Bindable_t *Bindable_t::GetObject (const char *binding) +{ + string s (binding ? binding : ""); + return GetObject (s); +} + +/***************************** +STATIC: Bindable_t::GetObject +*****************************/ + +Bindable_t *Bindable_t::GetObject (const string &binding) +{ + map<string, Bindable_t*>::const_iterator i = BindingBag.find (binding); + if (i != BindingBag.end()) + return i->second; + else + return NULL; +} + + +/********************** +Bindable_t::Bindable_t +**********************/ + +Bindable_t::Bindable_t() +{ + Binding = Bindable_t::CreateBinding(); + BindingBag [Binding] = this; +} + + + +/*********************** +Bindable_t::~Bindable_t +***********************/ + +Bindable_t::~Bindable_t() +{ + BindingBag.erase (Binding); +} + + +/***************************************************************************** + +$Id$ + +File: cmain.cpp +Date: 06Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + + +static EventMachine_t *EventMachine; +static int bUseEpoll = 0; +static int bUseKqueue = 0; + +extern "C" void ensure_eventmachine (const char *caller = "unknown caller") +{ + if (!EventMachine) { + const int err_size = 128; + char err_string[err_size]; + snprintf (err_string, err_size, "eventmachine not initialized: %s", caller); + #ifdef BUILD_FOR_RUBY + rb_raise(rb_eRuntimeError, err_string); + #else + throw std::runtime_error (err_string); + #endif + } +} + +/*********************** +evma_initialize_library +***********************/ + +extern "C" void evma_initialize_library (void(*cb)(const char*, int, const char*, int)) +{ + // Probably a bad idea to mess with the signal mask of a process + // we're just being linked into. + //InstallSignalHandlers(); + if (EventMachine) + #ifdef BUILD_FOR_RUBY + rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_initialize_library"); + #else + throw std::runtime_error ("eventmachine already initialized: evma_initialize_library"); + #endif + EventMachine = new EventMachine_t (cb); + if (bUseEpoll) + EventMachine->_UseEpoll(); + if (bUseKqueue) + EventMachine->_UseKqueue(); +} + + +/******************** +evma_release_library +********************/ + +extern "C" void evma_release_library() +{ + ensure_eventmachine("evma_release_library"); + delete EventMachine; + EventMachine = NULL; +} + + +/**************** +evma_run_machine +****************/ + +extern "C" void evma_run_machine() +{ + ensure_eventmachine("evma_run_machine"); + EventMachine->Run(); +} + + +/************************** +evma_install_oneshot_timer +**************************/ + +extern "C" const char *evma_install_oneshot_timer (int seconds) +{ + ensure_eventmachine("evma_install_oneshot_timer"); + return EventMachine->InstallOneshotTimer (seconds); +} + + +/********************** +evma_connect_to_server +**********************/ + +extern "C" const char *evma_connect_to_server (const char *server, int port) +{ + ensure_eventmachine("evma_connect_to_server"); + return EventMachine->ConnectToServer (server, port); +} + +/*************************** +evma_connect_to_unix_server +***************************/ + +extern "C" const char *evma_connect_to_unix_server (const char *server) +{ + ensure_eventmachine("evma_connect_to_unix_server"); + return EventMachine->ConnectToUnixServer (server); +} + +/************** +evma_attach_fd +**************/ + +extern "C" const char *evma_attach_fd (int file_descriptor, int notify_readable, int notify_writable) +{ + ensure_eventmachine("evma_attach_fd"); + return EventMachine->AttachFD (file_descriptor, (notify_readable ? true : false), (notify_writable ? true : false)); +} + +/************** +evma_detach_fd +**************/ + +extern "C" int evma_detach_fd (const char *binding) +{ + ensure_eventmachine("evma_dettach_fd"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) + return EventMachine->DetachFD (ed); + else + #ifdef BUILD_FOR_RUBY + rb_raise(rb_eRuntimeError, "invalid binding to detach"); + #else + throw std::runtime_error ("invalid binding to detach"); + #endif +} + +/********************** +evma_create_tcp_server +**********************/ + +extern "C" const char *evma_create_tcp_server (const char *address, int port) +{ + ensure_eventmachine("evma_create_tcp_server"); + return EventMachine->CreateTcpServer (address, port); +} + +/****************************** +evma_create_unix_domain_server +******************************/ + +extern "C" const char *evma_create_unix_domain_server (const char *filename) +{ + ensure_eventmachine("evma_create_unix_domain_server"); + return EventMachine->CreateUnixDomainServer (filename); +} + +/************************* +evma_open_datagram_socket +*************************/ + +extern "C" const char *evma_open_datagram_socket (const char *address, int port) +{ + ensure_eventmachine("evma_open_datagram_socket"); + return EventMachine->OpenDatagramSocket (address, port); +} + +/****************** +evma_open_keyboard +******************/ + +extern "C" const char *evma_open_keyboard() +{ + ensure_eventmachine("evma_open_keyboard"); + return EventMachine->OpenKeyboard(); +} + + + +/**************************** +evma_send_data_to_connection +****************************/ + +extern "C" int evma_send_data_to_connection (const char *binding, const char *data, int data_length) +{ + ensure_eventmachine("evma_send_data_to_connection"); + return ConnectionDescriptor::SendDataToConnection (binding, data, data_length); +} + +/****************** +evma_send_datagram +******************/ + +extern "C" int evma_send_datagram (const char *binding, const char *data, int data_length, const char *address, int port) +{ + ensure_eventmachine("evma_send_datagram"); + return DatagramDescriptor::SendDatagram (binding, data, data_length, address, port); +} + + +/********************* +evma_close_connection +*********************/ + +extern "C" void evma_close_connection (const char *binding, int after_writing) +{ + ensure_eventmachine("evma_close_connection"); + ConnectionDescriptor::CloseConnection (binding, (after_writing ? true : false)); +} + +/*********************************** +evma_report_connection_error_status +***********************************/ + +extern "C" int evma_report_connection_error_status (const char *binding) +{ + ensure_eventmachine("evma_report_connection_error_status"); + return ConnectionDescriptor::ReportErrorStatus (binding); +} + +/******************** +evma_stop_tcp_server +********************/ + +extern "C" void evma_stop_tcp_server (const char *binding) +{ + ensure_eventmachine("evma_stop_tcp_server"); + AcceptorDescriptor::StopAcceptor (binding); +} + + +/***************** +evma_stop_machine +*****************/ + +extern "C" void evma_stop_machine() +{ + ensure_eventmachine("evma_stop_machine"); + EventMachine->ScheduleHalt(); +} + + +/************** +evma_start_tls +**************/ + +extern "C" void evma_start_tls (const char *binding) +{ + ensure_eventmachine("evma_start_tls"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) + ed->StartTls(); +} + +/****************** +evma_set_tls_parms +******************/ + +extern "C" void evma_set_tls_parms (const char *binding, const char *privatekey_filename, const char *certchain_filename) +{ + ensure_eventmachine("evma_set_tls_parms"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) + ed->SetTlsParms (privatekey_filename, certchain_filename); +} + + +/***************** +evma_get_peername +*****************/ + +extern "C" int evma_get_peername (const char *binding, struct sockaddr *sa) +{ + ensure_eventmachine("evma_get_peername"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) { + return ed->GetPeername (sa) ? 1 : 0; + } + else + return 0; +} + +/***************** +evma_get_sockname +*****************/ + +extern "C" int evma_get_sockname (const char *binding, struct sockaddr *sa) +{ + ensure_eventmachine("evma_get_sockname"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) { + return ed->GetSockname (sa) ? 1 : 0; + } + else + return 0; +} + +/*********************** +evma_get_subprocess_pid +***********************/ + +extern "C" int evma_get_subprocess_pid (const char *binding, pid_t *pid) +{ + ensure_eventmachine("evma_get_subprocess_pid"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) { + return ed->GetSubprocessPid (pid) ? 1 : 0; + } + else + return 0; +} + +/************************** +evma_get_subprocess_status +**************************/ + +extern "C" int evma_get_subprocess_status (const char *binding, int *status) +{ + ensure_eventmachine("evma_get_subprocess_status"); + if (status) { + *status = EventMachine->SubprocessExitStatus; + return 1; + } + else + return 0; +} + + +/********************* +evma_signal_loopbreak +*********************/ + +extern "C" void evma_signal_loopbreak() +{ + ensure_eventmachine("evma_signal_loopbreak"); + EventMachine->SignalLoopBreaker(); +} + + + +/**************** +evma__write_file +****************/ + +extern "C" const char *evma__write_file (const char *filename) +{ + ensure_eventmachine("evma__write_file"); + return EventMachine->_OpenFileForWriting (filename); +} + + +/******************************** +evma_get_comm_inactivity_timeout +********************************/ + +extern "C" int evma_get_comm_inactivity_timeout (const char *binding, int *value) +{ + ensure_eventmachine("evma_get_comm_inactivity_timeout"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) { + return ed->GetCommInactivityTimeout (value); + } + else + return 0; //Perhaps this should be an exception. Access to an unknown binding. +} + +/******************************** +evma_set_comm_inactivity_timeout +********************************/ + +extern "C" int evma_set_comm_inactivity_timeout (const char *binding, int *value) +{ + ensure_eventmachine("evma_set_comm_inactivity_timeout"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) { + return ed->SetCommInactivityTimeout (value); + } + else + return 0; //Perhaps this should be an exception. Access to an unknown binding. +} + + +/********************** +evma_set_timer_quantum +**********************/ + +extern "C" void evma_set_timer_quantum (int interval) +{ + ensure_eventmachine("evma_set_timer_quantum"); + EventMachine->SetTimerQuantum (interval); +} + +/************************ +evma_set_max_timer_count +************************/ + +extern "C" void evma_set_max_timer_count (int ct) +{ + // This may only be called if the reactor is not running. + + if (EventMachine) + #ifdef BUILD_FOR_RUBY + rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_set_max_timer_count"); + #else + throw std::runtime_error ("eventmachine already initialized: evma_set_max_timer_count"); + #endif + EventMachine_t::SetMaxTimerCount (ct); +} + +/****************** +evma_setuid_string +******************/ + +extern "C" void evma_setuid_string (const char *username) +{ + // We do NOT need to be running an EM instance because this method is static. + EventMachine_t::SetuidString (username); +} + + +/********** +evma_popen +**********/ + +extern "C" const char *evma_popen (char * const*cmd_strings) +{ + ensure_eventmachine("evma_popen"); + return EventMachine->Socketpair (cmd_strings); +} + + +/*************************** +evma_get_outbound_data_size +***************************/ + +extern "C" int evma_get_outbound_data_size (const char *binding) +{ + ensure_eventmachine("evma_get_outbound_data_size"); + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + return ed ? ed->GetOutboundDataSize() : 0; +} + + +/*********** +evma__epoll +***********/ + +extern "C" void evma__epoll() +{ + bUseEpoll = 1; +} + +/************ +evma__kqueue +************/ + +extern "C" void evma__kqueue() +{ + bUseKqueue = 1; +} + + +/********************** +evma_set_rlimit_nofile +**********************/ + +extern "C" int evma_set_rlimit_nofile (int nofiles) +{ + return EventMachine_t::SetRlimitNofile (nofiles); +} + + +/********************************* +evma_send_file_data_to_connection +*********************************/ + +extern "C" int evma_send_file_data_to_connection (const char *binding, const char *filename) +{ + /* This is a sugaring over send_data_to_connection that reads a file into a + * locally-allocated buffer, and sends the file data to the remote peer. + * Return the number of bytes written to the caller. + * TODO, needs to impose a limit on the file size. This is intended only for + * small files. (I don't know, maybe 8K or less.) For larger files, use interleaved + * I/O to avoid slowing the rest of the system down. + * TODO: we should return a code rather than barf, in case of file-not-found. + * TODO, does this compile on Windows? + * TODO, given that we want this to work only with small files, how about allocating + * the buffer on the stack rather than the heap? + * + * Modified 25Jul07. This now returns -1 on file-too-large; 0 for success, and a positive + * errno in case of other errors. + * + /* Contributed by Kirk Haines. + */ + + char data[32*1024]; + int r; + + ensure_eventmachine("evma_send_file_data_to_connection"); + + int Fd = open (filename, O_RDONLY); + + if (Fd < 0) + return errno; + // From here on, all early returns MUST close Fd. + + struct stat st; + if (fstat (Fd, &st)) { + int e = errno; + close (Fd); + return e; + } + + int filesize = st.st_size; + if (filesize <= 0) { + close (Fd); + return 0; + } + else if (filesize > sizeof(data)) { + close (Fd); + return -1; + } + + + r = read (Fd, data, filesize); + if (r != filesize) { + int e = errno; + close (Fd); + return e; + } + evma_send_data_to_connection (binding, data, r); + close (Fd); + + return 0; +} + +/***************************************************************************** + +$Id$ + +File: cplusplus.cpp +Date: 27Jul07 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + + +#include "project.h" + + +namespace EM { + static map<string, Eventable*> Eventables; + static map<string, void(*)()> Timers; +} + + +/******* +EM::Run +*******/ + +void EM::Run (void (*start_func)()) +{ + evma__epoll(); + evma_initialize_library (EM::Callback); + if (start_func) + AddTimer (0, start_func); + evma_run_machine(); + evma_release_library(); +} + +/************ +EM::AddTimer +************/ + +void EM::AddTimer (int milliseconds, void (*func)()) +{ + if (func) { + const char *sig = evma_install_oneshot_timer (milliseconds); + Timers.insert (make_pair (sig, func)); + } +} + + +/*************** +EM::StopReactor +***************/ + +void EM::StopReactor() +{ + evma_stop_machine(); +} + + +/******************** +EM::Acceptor::Accept +********************/ + +void EM::Acceptor::Accept (const char *signature) +{ + Connection *c = MakeConnection(); + c->Signature = signature; + Eventables.insert (make_pair (c->Signature, c)); + c->PostInit(); +} + +/************************ +EM::Connection::SendData +************************/ + +void EM::Connection::SendData (const char *data) +{ + if (data) + SendData (data, strlen (data)); +} + + +/************************ +EM::Connection::SendData +************************/ + +void EM::Connection::SendData (const char *data, int length) +{ + evma_send_data_to_connection (Signature.c_str(), data, length); +} + + +/********************* +EM::Connection::Close +*********************/ + +void EM::Connection::Close (bool afterWriting) +{ + evma_close_connection (Signature.c_str(), afterWriting); +} + + +/*********************** +EM::Connection::Connect +***********************/ + +void EM::Connection::Connect (const char *host, int port) +{ + Signature = evma_connect_to_server (host, port); + Eventables.insert( make_pair (Signature, this)); +} + +/******************* +EM::Acceptor::Start +*******************/ + +void EM::Acceptor::Start (const char *host, int port) +{ + Signature = evma_create_tcp_server (host, port); + Eventables.insert( make_pair (Signature, this)); +} + + + +/************ +EM::Callback +************/ + +void EM::Callback (const char *sig, int ev, const char *data, int length) +{ + EM::Eventable *e; + void (*f)(); + + switch (ev) { + case EM_TIMER_FIRED: + f = Timers [data]; + if (f) + (*f)(); + Timers.erase (sig); + break; + + case EM_CONNECTION_READ: + e = EM::Eventables [sig]; + e->ReceiveData (data, length); + break; + + case EM_CONNECTION_COMPLETED: + e = EM::Eventables [sig]; + e->ConnectionCompleted(); + break; + + case EM_CONNECTION_ACCEPTED: + e = EM::Eventables [sig]; + e->Accept (data); + break; + + case EM_CONNECTION_UNBOUND: + e = EM::Eventables [sig]; + e->Unbind(); + EM::Eventables.erase (sig); + delete e; + break; + } +} + +/***************************************************************************** + +$Id$ + +File: ed.cpp +Date: 06Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + + + +/******************** +SetSocketNonblocking +********************/ + +bool SetSocketNonblocking (SOCKET sd) +{ + #ifdef OS_UNIX + int val = fcntl (sd, F_GETFL, 0); + return (fcntl (sd, F_SETFL, val | O_NONBLOCK) != SOCKET_ERROR) ? true : false; + #endif + + #ifdef OS_WIN32 + unsigned long one = 1; + return (ioctlsocket (sd, FIONBIO, &one) == 0) ? true : false; + #endif +} + + +/**************************************** +EventableDescriptor::EventableDescriptor +****************************************/ + +EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em): + bCloseNow (false), + bCloseAfterWriting (false), + MySocket (sd), + EventCallback (NULL), + LastRead (0), + LastWritten (0), + bCallbackUnbind (true), + MyEventMachine (em) +{ + /* There are three ways to close a socket, all of which should + * automatically signal to the event machine that this object + * should be removed from the polling scheduler. + * First is a hard close, intended for bad errors or possible + * security violations. It immediately closes the connection + * and puts this object into an error state. + * Second is to set bCloseNow, which will cause the event machine + * to delete this object (and thus close the connection in our + * destructor) the next chance it gets. bCloseNow also inhibits + * the writing of new data on the socket (but not necessarily + * the reading of new data). + * The third way is to set bCloseAfterWriting, which inhibits + * the writing of new data and converts to bCloseNow as soon + * as everything in the outbound queue has been written. + * bCloseAfterWriting is really for use only by protocol handlers + * (for example, HTTP writes an HTML page and then closes the + * connection). All of the error states we generate internally + * cause an immediate close to be scheduled, which may have the + * effect of discarding outbound data. + */ + + if (sd == INVALID_SOCKET) + throw std::runtime_error ("bad eventable descriptor"); + if (MyEventMachine == NULL) + throw std::runtime_error ("bad em in eventable descriptor"); + CreatedAt = gCurrentLoopTime; + + #ifdef HAVE_EPOLL + EpollEvent.data.ptr = this; + #endif +} + + +/***************************************** +EventableDescriptor::~EventableDescriptor +*****************************************/ + +EventableDescriptor::~EventableDescriptor() +{ + if (EventCallback && bCallbackUnbind) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_UNBOUND, NULL, 0); + Close(); +} + + +/************************************* +EventableDescriptor::SetEventCallback +*************************************/ + +void EventableDescriptor::SetEventCallback (void(*cb)(const char*, int, const char*, int)) +{ + EventCallback = cb; +} + + +/************************** +EventableDescriptor::Close +**************************/ + +void EventableDescriptor::Close() +{ + // Close the socket right now. Intended for emergencies. + if (MySocket != INVALID_SOCKET) { + shutdown (MySocket, 1); + closesocket (MySocket); + MySocket = INVALID_SOCKET; + } +} + + +/********************************* +EventableDescriptor::ShouldDelete +*********************************/ + +bool EventableDescriptor::ShouldDelete() +{ + /* For use by a socket manager, which needs to know if this object + * should be removed from scheduling events and deleted. + * Has an immediate close been scheduled, or are we already closed? + * If either of these are the case, return true. In theory, the manager will + * then delete us, which in turn will make sure the socket is closed. + * Note, if bCloseAfterWriting is true, we check a virtual method to see + * if there is outbound data to write, and only request a close if there is none. + */ + + return ((MySocket == INVALID_SOCKET) || bCloseNow || (bCloseAfterWriting && (GetOutboundDataSize() <= 0))); +} + + +/********************************** +EventableDescriptor::ScheduleClose +**********************************/ + +void EventableDescriptor::ScheduleClose (bool after_writing) +{ + // KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled. + if (after_writing) + bCloseAfterWriting = true; + else + bCloseNow = true; +} + + +/************************************* +EventableDescriptor::IsCloseScheduled +*************************************/ + +bool EventableDescriptor::IsCloseScheduled() +{ + // KEEP THIS SYNCHRONIZED WITH ::ScheduleClose. + return (bCloseNow || bCloseAfterWriting); +} + + +/****************************************** +ConnectionDescriptor::ConnectionDescriptor +******************************************/ + +ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em): + EventableDescriptor (sd, em), + bConnectPending (false), + bNotifyReadable (false), + bNotifyWritable (false), + bReadAttemptedAfterClose (false), + bWriteAttemptedAfterClose (false), + OutboundDataSize (0), + #ifdef WITH_SSL + SslBox (NULL), + #endif + bIsServer (false), + LastIo (gCurrentLoopTime), + InactivityTimeout (0) +{ + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLOUT; + #endif + // 22Jan09: Moved ArmKqueueWriter into SetConnectPending() to fix assertion failure in _WriteOutboundData() +} + + +/******************************************* +ConnectionDescriptor::~ConnectionDescriptor +*******************************************/ + +ConnectionDescriptor::~ConnectionDescriptor() +{ + // Run down any stranded outbound data. + for (size_t i=0; i < OutboundPages.size(); i++) + OutboundPages[i].Free(); + + #ifdef WITH_SSL + if (SslBox) + delete SslBox; + #endif +} + + +/************************************************** +STATIC: ConnectionDescriptor::SendDataToConnection +**************************************************/ + +int ConnectionDescriptor::SendDataToConnection (const char *binding, const char *data, int data_length) +{ + // TODO: This is something of a hack, or at least it's a static method of the wrong class. + // TODO: Poor polymorphism here. We should be calling one virtual method + // instead of hacking out the runtime information of the target object. + ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding)); + if (cd) + return cd->SendOutboundData (data, data_length); + DatagramDescriptor *ds = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding)); + if (ds) + return ds->SendOutboundData (data, data_length); + #ifdef OS_UNIX + PipeDescriptor *ps = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding)); + if (ps) + return ps->SendOutboundData (data, data_length); + #endif + return -1; +} + + +/********************************************* +STATIC: ConnectionDescriptor::CloseConnection +*********************************************/ + +void ConnectionDescriptor::CloseConnection (const char *binding, bool after_writing) +{ + // TODO: This is something of a hack, or at least it's a static method of the wrong class. + EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding)); + if (ed) + ed->ScheduleClose (after_writing); +} + +/*********************************************** +STATIC: ConnectionDescriptor::ReportErrorStatus +***********************************************/ + +int ConnectionDescriptor::ReportErrorStatus (const char *binding) +{ + // TODO: This is something of a hack, or at least it's a static method of the wrong class. + // TODO: Poor polymorphism here. We should be calling one virtual method + // instead of hacking out the runtime information of the target object. + ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding)); + if (cd) + return cd->_ReportErrorStatus(); + return -1; +} + +/*************************************** +ConnectionDescriptor::SetConnectPending +****************************************/ + +void ConnectionDescriptor::SetConnectPending(bool f) +{ + bConnectPending = f; + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueWriter (this); + #endif +} + + +/************************************** +ConnectionDescriptor::SendOutboundData +**************************************/ + +int ConnectionDescriptor::SendOutboundData (const char *data, int length) +{ + #ifdef WITH_SSL + if (SslBox) { + if (length > 0) { + int w = SslBox->PutPlaintext (data, length); + if (w < 0) + ScheduleClose (false); + else + _DispatchCiphertext(); + } + // TODO: What's the correct return value? + return 1; // That's a wild guess, almost certainly wrong. + } + else + #endif + return _SendRawOutboundData (data, length); +} + + + +/****************************************** +ConnectionDescriptor::_SendRawOutboundData +******************************************/ + +int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length) +{ + /* This internal method is called to schedule bytes that + * will be sent out to the remote peer. + * It's not directly accessed by the caller, who hits ::SendOutboundData, + * which may or may not filter or encrypt the caller's data before + * sending it here. + */ + + // Highly naive and incomplete implementation. + // There's no throttle for runaways (which should abort only this connection + // and not the whole process), and no coalescing of small pages. + // (Well, not so bad, small pages are coalesced in ::Write) + + if (IsCloseScheduled()) + //if (bCloseNow || bCloseAfterWriting) + return 0; + + if (!data && (length > 0)) + throw std::runtime_error ("bad outbound data"); + char *buffer = (char *) malloc (length + 1); + if (!buffer) + throw std::runtime_error ("no allocation for outbound data"); + memcpy (buffer, data, length); + buffer [length] = 0; + OutboundPages.push_back (OutboundPage (buffer, length)); + OutboundDataSize += length; + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | EPOLLOUT); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueWriter (this); + #endif + return length; +} + + + +/*********************************** +ConnectionDescriptor::SelectForRead +***********************************/ + +bool ConnectionDescriptor::SelectForRead() +{ + /* A connection descriptor is always scheduled for read, + * UNLESS it's in a pending-connect state. + * On Linux, unlike Unix, a nonblocking socket on which + * connect has been called, does NOT necessarily select + * both readable and writable in case of error. + * The socket will select writable when the disposition + * of the connect is known. On the other hand, a socket + * which successfully connects and selects writable may + * indeed have some data available on it, so it will + * select readable in that case, violating expectations! + * So we will not poll for readability until the socket + * is known to be in a connected state. + */ + + return bConnectPending ? false : true; +} + + +/************************************ +ConnectionDescriptor::SelectForWrite +************************************/ + +bool ConnectionDescriptor::SelectForWrite() +{ + /* Cf the notes under SelectForRead. + * In a pending-connect state, we ALWAYS select for writable. + * In a normal state, we only select for writable when we + * have outgoing data to send. + */ + + if (bConnectPending || bNotifyWritable) + return true; + else { + return (GetOutboundDataSize() > 0); + } +} + + +/************************** +ConnectionDescriptor::Read +**************************/ + +void ConnectionDescriptor::Read() +{ + /* Read and dispatch data on a socket that has selected readable. + * It's theoretically possible to get and dispatch incoming data on + * a socket that has already been scheduled for closing or close-after-writing. + * In those cases, we'll leave it up the to protocol handler to "do the + * right thing" (which probably means to ignore the incoming data). + * + * 22Aug06: Chris Ochs reports that on FreeBSD, it's possible to come + * here with the socket already closed, after the process receives + * a ctrl-C signal (not sure if that's TERM or INT on BSD). The application + * was one in which network connections were doing a lot of interleaved reads + * and writes. + * Since we always write before reading (in order to keep the outbound queues + * as light as possible), I think what happened is that an interrupt caused + * the socket to be closed in ConnectionDescriptor::Write. We'll then + * come here in the same pass through the main event loop, and won't get + * cleaned up until immediately after. + * We originally asserted that the socket was valid when we got here. + * To deal properly with the possibility that we are closed when we get here, + * I removed the assert. HOWEVER, the potential for an infinite loop scares me, + * so even though this is really clunky, I added a flag to assert that we never + * come here more than once after being closed. (FCianfrocca) + */ + + int sd = GetSocket(); + //assert (sd != INVALID_SOCKET); (original, removed 22Aug06) + if (sd == INVALID_SOCKET) { + assert (!bReadAttemptedAfterClose); + bReadAttemptedAfterClose = true; + return; + } + + if (bNotifyReadable) { + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_NOTIFY_READABLE, NULL, 0); + return; + } + + LastIo = gCurrentLoopTime; + + int total_bytes_read = 0; + char readbuffer [16 * 1024 + 1]; + + for (int i=0; i < 10; i++) { + // Don't read just one buffer and then move on. This is faster + // if there is a lot of incoming. + // But don't read indefinitely. Give other sockets a chance to run. + // NOTICE, we're reading one less than the buffer size. + // That's so we can put a guard byte at the end of what we send + // to user code. + + + int r = recv (sd, readbuffer, sizeof(readbuffer) - 1, 0); + //cerr << "<R:" << r << ">"; + + if (r > 0) { + total_bytes_read += r; + LastRead = gCurrentLoopTime; + + // Add a null-terminator at the the end of the buffer + // that we will send to the callback. + // DO NOT EVER CHANGE THIS. We want to explicitly allow users + // to be able to depend on this behavior, so they will have + // the option to do some things faster. Additionally it's + // a security guard against buffer overflows. + readbuffer [r] = 0; + _DispatchInboundData (readbuffer, r); + } + else if (r == 0) { + break; + } + else { + // Basically a would-block, meaning we've read everything there is to read. + break; + } + + } + + + if (total_bytes_read == 0) { + // If we read no data on a socket that selected readable, + // it generally means the other end closed the connection gracefully. + ScheduleClose (false); + //bCloseNow = true; + } + +} + + + +/****************************************** +ConnectionDescriptor::_DispatchInboundData +******************************************/ + +void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size) +{ + #ifdef WITH_SSL + if (SslBox) { + SslBox->PutCiphertext (buffer, size); + + int s; + char B [2048]; + while ((s = SslBox->GetPlaintext (B, sizeof(B) - 1)) > 0) { + B [s] = 0; + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, B, s); + } + // INCOMPLETE, s may indicate an SSL error that would force the connection down. + _DispatchCiphertext(); + } + else { + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, buffer, size); + } + #endif + + #ifdef WITHOUT_SSL + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, buffer, size); + #endif +} + + + + + +/*************************** +ConnectionDescriptor::Write +***************************/ + +void ConnectionDescriptor::Write() +{ + /* A socket which is in a pending-connect state will select + * writable when the disposition of the connect is known. + * At that point, check to be sure there are no errors, + * and if none, then promote the socket out of the pending + * state. + * TODO: I haven't figured out how Windows signals errors on + * unconnected sockets. Maybe it does the untraditional but + * logical thing and makes the socket selectable for error. + * If so, it's unsupported here for the time being, and connect + * errors will have to be caught by the timeout mechanism. + */ + + if (bConnectPending) { + int error; + socklen_t len; + len = sizeof(error); + #ifdef OS_UNIX + int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len); + #endif + #ifdef OS_WIN32 + int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len); + #endif + if ((o == 0) && (error == 0)) { + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_COMPLETED, "", 0); + bConnectPending = false; + #ifdef HAVE_EPOLL + // The callback may have scheduled outbound data. + EpollEvent.events = EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0); + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueReader (this); + // The callback may have scheduled outbound data. + if (SelectForWrite()) + MyEventMachine->ArmKqueueWriter (this); + #endif + } + else + ScheduleClose (false); + //bCloseNow = true; + } + else { + + if (bNotifyWritable) { + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0); + return; + } + + _WriteOutboundData(); + } +} + + +/**************************************** +ConnectionDescriptor::_WriteOutboundData +****************************************/ + +void ConnectionDescriptor::_WriteOutboundData() +{ + /* This is a helper function called by ::Write. + * It's possible for a socket to select writable and then no longer + * be writable by the time we get around to writing. The kernel might + * have used up its available output buffers between the select call + * and when we get here. So this condition is not an error. + * + * 20Jul07, added the same kind of protection against an invalid socket + * that is at the top of ::Read. Not entirely how this could happen in + * real life (connection-reset from the remote peer, perhaps?), but I'm + * doing it to address some reports of crashing under heavy loads. + */ + + int sd = GetSocket(); + //assert (sd != INVALID_SOCKET); + if (sd == INVALID_SOCKET) { + assert (!bWriteAttemptedAfterClose); + bWriteAttemptedAfterClose = true; + return; + } + + LastIo = gCurrentLoopTime; + char output_buffer [16 * 1024]; + size_t nbytes = 0; + + while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) { + OutboundPage *op = &(OutboundPages[0]); + if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) { + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset); + nbytes += (op->Length - op->Offset); + op->Free(); + OutboundPages.pop_front(); + } + else { + int len = sizeof(output_buffer) - nbytes; + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len); + op->Offset += len; + nbytes += len; + } + } + + // We should never have gotten here if there were no data to write, + // so assert that as a sanity check. + // Don't bother to make sure nbytes is less than output_buffer because + // if it were we probably would have crashed already. + assert (nbytes > 0); + + assert (GetSocket() != INVALID_SOCKET); + int bytes_written = send (GetSocket(), output_buffer, nbytes, 0); + + bool err = false; + if (bytes_written < 0) { + err = true; + bytes_written = 0; + } + + assert (bytes_written >= 0); + OutboundDataSize -= bytes_written; + if ((size_t)bytes_written < nbytes) { + int len = nbytes - bytes_written; + char *buffer = (char*) malloc (len + 1); + if (!buffer) + throw std::runtime_error ("bad alloc throwing back data"); + memcpy (buffer, output_buffer + bytes_written, len); + buffer [len] = 0; + OutboundPages.push_front (OutboundPage (buffer, len)); + } + + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + #ifdef HAVE_KQUEUE + if (SelectForWrite()) + MyEventMachine->ArmKqueueWriter (this); + #endif + + + if (err) { + #ifdef OS_UNIX + if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) + #endif + #ifdef OS_WIN32 + if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) + #endif + Close(); + } +} + + +/**************************************** +ConnectionDescriptor::_ReportErrorStatus +****************************************/ + +int ConnectionDescriptor::_ReportErrorStatus() +{ + int error; + socklen_t len; + len = sizeof(error); + #ifdef OS_UNIX + int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len); + #endif + #ifdef OS_WIN32 + int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len); + #endif + if ((o == 0) && (error == 0)) + return 0; + else + return 1; +} + + +/****************************** +ConnectionDescriptor::StartTls +******************************/ + +void ConnectionDescriptor::StartTls() +{ + #ifdef WITH_SSL + if (SslBox) + throw std::runtime_error ("SSL/TLS already running on connection"); + + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename); + _DispatchCiphertext(); + #endif + + #ifdef WITHOUT_SSL + throw std::runtime_error ("Encryption not available on this event-machine"); + #endif +} + + +/********************************* +ConnectionDescriptor::SetTlsParms +*********************************/ + +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename) +{ + #ifdef WITH_SSL + if (SslBox) + throw std::runtime_error ("call SetTlsParms before calling StartTls"); + if (privkey_filename && *privkey_filename) + PrivateKeyFilename = privkey_filename; + if (certchain_filename && *certchain_filename) + CertChainFilename = certchain_filename; + #endif + + #ifdef WITHOUT_SSL + throw std::runtime_error ("Encryption not available on this event-machine"); + #endif +} + + + +/***************************************** +ConnectionDescriptor::_DispatchCiphertext +*****************************************/ +#ifdef WITH_SSL +void ConnectionDescriptor::_DispatchCiphertext() +{ + assert (SslBox); + + + char BigBuf [2048]; + bool did_work; + + do { + did_work = false; + + // try to drain ciphertext + while (SslBox->CanGetCiphertext()) { + int r = SslBox->GetCiphertext (BigBuf, sizeof(BigBuf)); + assert (r > 0); + _SendRawOutboundData (BigBuf, r); + did_work = true; + } + + // Pump the SslBox, in case it has queued outgoing plaintext + // This will return >0 if data was written, + // 0 if no data was written, and <0 if there was a fatal error. + bool pump; + do { + pump = false; + int w = SslBox->PutPlaintext (NULL, 0); + if (w > 0) { + did_work = true; + pump = true; + } + else if (w < 0) + ScheduleClose (false); + } while (pump); + + // try to put plaintext. INCOMPLETE, doesn't belong here? + // In SendOutboundData, we're spooling plaintext directly + // into SslBox. That may be wrong, we may need to buffer it + // up here! + /* + const char *ptr; + int ptr_length; + while (OutboundPlaintext.GetPage (&ptr, &ptr_length)) { + assert (ptr && (ptr_length > 0)); + int w = SslMachine.PutPlaintext (ptr, ptr_length); + if (w > 0) { + OutboundPlaintext.DiscardBytes (w); + did_work = true; + } + else + break; + } + */ + + } while (did_work); + +} +#endif + + + +/******************************* +ConnectionDescriptor::Heartbeat +*******************************/ + +void ConnectionDescriptor::Heartbeat() +{ + /* Only allow a certain amount of time to go by while waiting + * for a pending connect. If it expires, then kill the socket. + * For a connected socket, close it if its inactivity timer + * has expired. + */ + + if (bConnectPending) { + if ((gCurrentLoopTime - CreatedAt) >= PendingConnectTimeout) + ScheduleClose (false); + //bCloseNow = true; + } + else { + if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) + ScheduleClose (false); + //bCloseNow = true; + } +} + + +/**************************************** +LoopbreakDescriptor::LoopbreakDescriptor +****************************************/ + +LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em): + EventableDescriptor (sd, parent_em) +{ + /* This is really bad and ugly. Change someday if possible. + * We have to know about an event-machine (probably the one that owns us), + * so we can pass newly-created connections to it. + */ + + bCallbackUnbind = false; + + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueReader (this); + #endif +} + + + + +/************************* +LoopbreakDescriptor::Read +*************************/ + +void LoopbreakDescriptor::Read() +{ + // TODO, refactor, this code is probably in the wrong place. + assert (MyEventMachine); + MyEventMachine->_ReadLoopBreaker(); +} + + +/************************** +LoopbreakDescriptor::Write +**************************/ + +void LoopbreakDescriptor::Write() +{ + // Why are we here? + throw std::runtime_error ("bad code path in loopbreak"); +} + +/************************************** +AcceptorDescriptor::AcceptorDescriptor +**************************************/ + +AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em): + EventableDescriptor (sd, parent_em) +{ + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueReader (this); + #endif +} + + +/*************************************** +AcceptorDescriptor::~AcceptorDescriptor +***************************************/ + +AcceptorDescriptor::~AcceptorDescriptor() +{ +} + +/**************************************** +STATIC: AcceptorDescriptor::StopAcceptor +****************************************/ + +void AcceptorDescriptor::StopAcceptor (const char *binding) +{ + // TODO: This is something of a hack, or at least it's a static method of the wrong class. + AcceptorDescriptor *ad = dynamic_cast <AcceptorDescriptor*> (Bindable_t::GetObject (binding)); + if (ad) + ad->ScheduleClose (false); + else + throw std::runtime_error ("failed to close nonexistent acceptor"); +} + + +/************************ +AcceptorDescriptor::Read +************************/ + +void AcceptorDescriptor::Read() +{ + /* Accept up to a certain number of sockets on the listening connection. + * Don't try to accept all that are present, because this would allow a DoS attack + * in which no data were ever read or written. We should accept more than one, + * if available, to keep the partially accepted sockets from backing up in the kernel. + */ + + /* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it + * for readability. According to Stevens UNP, it's possible for an acceptor to select readable + * and then block when we call accept. For example, the other end resets the connection after + * the socket selects readable and before we call accept. The kernel will remove the dead + * socket from the accept queue. If the accept queue is now empty, accept will block. + */ + + + struct sockaddr_in pin; + socklen_t addrlen = sizeof (pin); + + for (int i=0; i < 10; i++) { + int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen); + if (sd == INVALID_SOCKET) { + // This breaks the loop when we've accepted everything on the kernel queue, + // up to 10 new connections. But what if the *first* accept fails? + // Does that mean anything serious is happening, beyond the situation + // described in the note above? + break; + } + + // Set the newly-accepted socket non-blocking. + // On Windows, this may fail because, weirdly, Windows inherits the non-blocking + // attribute that we applied to the acceptor socket into the accepted one. + if (!SetSocketNonblocking (sd)) { + //int val = fcntl (sd, F_GETFL, 0); + //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) { + shutdown (sd, 1); + closesocket (sd); + continue; + } + + + // Disable slow-start (Nagle algorithm). Eventually make this configurable. + int one = 1; + setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)); + + + ConnectionDescriptor *cd = new ConnectionDescriptor (sd, MyEventMachine); + if (!cd) + throw std::runtime_error ("no newly accepted connection"); + cd->SetServerMode(); + if (EventCallback) { + (*EventCallback) (GetBinding().c_str(), EM_CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size()); + } + #ifdef HAVE_EPOLL + cd->GetEpollEvent()->events = EPOLLIN | (cd->SelectForWrite() ? EPOLLOUT : 0); + #endif + assert (MyEventMachine); + MyEventMachine->Add (cd); + #ifdef HAVE_KQUEUE + if (cd->SelectForWrite()) + MyEventMachine->ArmKqueueWriter (cd); + MyEventMachine->ArmKqueueReader (cd); + #endif + } + +} + + +/************************* +AcceptorDescriptor::Write +*************************/ + +void AcceptorDescriptor::Write() +{ + // Why are we here? + throw std::runtime_error ("bad code path in acceptor"); +} + + +/***************************** +AcceptorDescriptor::Heartbeat +*****************************/ + +void AcceptorDescriptor::Heartbeat() +{ + // No-op +} + + +/******************************* +AcceptorDescriptor::GetSockname +*******************************/ + +bool AcceptorDescriptor::GetSockname (struct sockaddr *s) +{ + bool ok = false; + if (s) { + socklen_t len = sizeof(*s); + int gp = getsockname (GetSocket(), s, &len); + if (gp == 0) + ok = true; + } + return ok; +} + + + +/************************************** +DatagramDescriptor::DatagramDescriptor +**************************************/ + +DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em): + EventableDescriptor (sd, parent_em), + OutboundDataSize (0), + LastIo (gCurrentLoopTime), + InactivityTimeout (0) +{ + memset (&ReturnAddress, 0, sizeof(ReturnAddress)); + + /* Provisionally added 19Oct07. All datagram sockets support broadcasting. + * Until now, sending to a broadcast address would give EACCES (permission denied) + * on systems like Linux and BSD that require the SO_BROADCAST socket-option in order + * to accept a packet to a broadcast address. Solaris doesn't require it. I think + * Windows DOES require it but I'm not sure. + * + * Ruby does NOT do what we're doing here. In Ruby, you have to explicitly set SO_BROADCAST + * on a UDP socket in order to enable broadcasting. The reason for requiring the option + * in the first place is so that applications don't send broadcast datagrams by mistake. + * I imagine that could happen if a user of an application typed in an address that happened + * to be a broadcast address on that particular subnet. + * + * This is provisional because someone may eventually come up with a good reason not to + * do it for all UDP sockets. If that happens, then we'll need to add a usercode-level API + * to set the socket option, just like Ruby does. AND WE'LL ALSO BREAK CODE THAT DOESN'T + * EXPLICITLY SET THE OPTION. + */ + + int oval = 1; + int sob = setsockopt (GetSocket(), SOL_SOCKET, SO_BROADCAST, (char*)&oval, sizeof(oval)); + + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueReader (this); + #endif +} + + +/*************************************** +DatagramDescriptor::~DatagramDescriptor +***************************************/ + +DatagramDescriptor::~DatagramDescriptor() +{ + // Run down any stranded outbound data. + for (size_t i=0; i < OutboundPages.size(); i++) + OutboundPages[i].Free(); +} + + +/***************************** +DatagramDescriptor::Heartbeat +*****************************/ + +void DatagramDescriptor::Heartbeat() +{ + // Close it if its inactivity timer has expired. + + if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) + ScheduleClose (false); + //bCloseNow = true; +} + + +/************************ +DatagramDescriptor::Read +************************/ + +void DatagramDescriptor::Read() +{ + int sd = GetSocket(); + assert (sd != INVALID_SOCKET); + LastIo = gCurrentLoopTime; + + // This is an extremely large read buffer. + // In many cases you wouldn't expect to get any more than 4K. + char readbuffer [16 * 1024]; + + for (int i=0; i < 10; i++) { + // Don't read just one buffer and then move on. This is faster + // if there is a lot of incoming. + // But don't read indefinitely. Give other sockets a chance to run. + // NOTICE, we're reading one less than the buffer size. + // That's so we can put a guard byte at the end of what we send + // to user code. + + struct sockaddr_in sin; + socklen_t slen = sizeof (sin); + memset (&sin, 0, slen); + + int r = recvfrom (sd, readbuffer, sizeof(readbuffer) - 1, 0, (struct sockaddr*)&sin, &slen); + //cerr << "<R:" << r << ">"; + + // In UDP, a zero-length packet is perfectly legal. + if (r >= 0) { + LastRead = gCurrentLoopTime; + + // Add a null-terminator at the the end of the buffer + // that we will send to the callback. + // DO NOT EVER CHANGE THIS. We want to explicitly allow users + // to be able to depend on this behavior, so they will have + // the option to do some things faster. Additionally it's + // a security guard against buffer overflows. + readbuffer [r] = 0; + + + // Set up a "temporary" return address so that callers can "reply" to us + // from within the callback we are about to invoke. That means that ordinary + // calls to "send_data_to_connection" (which is of course misnamed in this + // case) will result in packets being sent back to the same place that sent + // us this one. + // There is a different call (evma_send_datagram) for cases where the caller + // actually wants to send a packet somewhere else. + + memset (&ReturnAddress, 0, sizeof(ReturnAddress)); + memcpy (&ReturnAddress, &sin, slen); + + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r); + + } + else { + // Basically a would-block, meaning we've read everything there is to read. + break; + } + + } + + +} + + +/************************* +DatagramDescriptor::Write +*************************/ + +void DatagramDescriptor::Write() +{ + /* It's possible for a socket to select writable and then no longer + * be writable by the time we get around to writing. The kernel might + * have used up its available output buffers between the select call + * and when we get here. So this condition is not an error. + * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData, + * but differs in the that the outbound data pages (received from the + * user) are _message-structured._ That is, we send each of them out + * one message at a time. + * TODO, we are currently suppressing the EMSGSIZE error!!! + */ + + int sd = GetSocket(); + assert (sd != INVALID_SOCKET); + LastIo = gCurrentLoopTime; + + assert (OutboundPages.size() > 0); + + // Send out up to 10 packets, then cycle the machine. + for (int i = 0; i < 10; i++) { + if (OutboundPages.size() <= 0) + break; + OutboundPage *op = &(OutboundPages[0]); + + // The nasty cast to (char*) is needed because Windows is brain-dead. + int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From)); + int e = errno; + + OutboundDataSize -= op->Length; + op->Free(); + OutboundPages.pop_front(); + + if (s == SOCKET_ERROR) { + #ifdef OS_UNIX + if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) { + #endif + #ifdef OS_WIN32 + if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) { + #endif + Close(); + break; + } + } + } + + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif +} + + +/********************************** +DatagramDescriptor::SelectForWrite +**********************************/ + +bool DatagramDescriptor::SelectForWrite() +{ + /* Changed 15Nov07, per bug report by Mark Zvillius. + * The outbound data size will be zero if there are zero-length outbound packets, + * so we now select writable in case the outbound page buffer is not empty. + * Note that the superclass ShouldDelete method still checks for outbound data size, + * which may be wrong. + */ + //return (GetOutboundDataSize() > 0); (Original) + return (OutboundPages.size() > 0); +} + + +/************************************ +DatagramDescriptor::SendOutboundData +************************************/ + +int DatagramDescriptor::SendOutboundData (const char *data, int length) +{ + // This is an exact clone of ConnectionDescriptor::SendOutboundData. + // That means it needs to move to a common ancestor. + + if (IsCloseScheduled()) + //if (bCloseNow || bCloseAfterWriting) + return 0; + + if (!data && (length > 0)) + throw std::runtime_error ("bad outbound data"); + char *buffer = (char *) malloc (length + 1); + if (!buffer) + throw std::runtime_error ("no allocation for outbound data"); + memcpy (buffer, data, length); + buffer [length] = 0; + OutboundPages.push_back (OutboundPage (buffer, length, ReturnAddress)); + OutboundDataSize += length; + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | EPOLLOUT); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + return length; +} + + +/**************************************** +DatagramDescriptor::SendOutboundDatagram +****************************************/ + +int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, const char *address, int port) +{ + // This is an exact clone of ConnectionDescriptor::SendOutboundData. + // That means it needs to move to a common ancestor. + // TODO: Refactor this so there's no overlap with SendOutboundData. + + if (IsCloseScheduled()) + //if (bCloseNow || bCloseAfterWriting) + return 0; + + if (!address || !*address || !port) + return 0; + + sockaddr_in pin; + unsigned long HostAddr; + + HostAddr = inet_addr (address); + if (HostAddr == INADDR_NONE) { + // The nasty cast to (char*) is because Windows is brain-dead. + hostent *hp = gethostbyname ((char*)address); + if (!hp) + return 0; + HostAddr = ((in_addr*)(hp->h_addr))->s_addr; + } + + memset (&pin, 0, sizeof(pin)); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = HostAddr; + pin.sin_port = htons (port); + + + + if (!data && (length > 0)) + throw std::runtime_error ("bad outbound data"); + char *buffer = (char *) malloc (length + 1); + if (!buffer) + throw std::runtime_error ("no allocation for outbound data"); + memcpy (buffer, data, length); + buffer [length] = 0; + OutboundPages.push_back (OutboundPage (buffer, length, pin)); + OutboundDataSize += length; + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | EPOLLOUT); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + return length; +} + + +/**************************************** +STATIC: DatagramDescriptor::SendDatagram +****************************************/ + +int DatagramDescriptor::SendDatagram (const char *binding, const char *data, int length, const char *address, int port) +{ + DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding)); + if (dd) + return dd->SendOutboundDatagram (data, length, address, port); + else + return -1; +} + + +/********************************* +ConnectionDescriptor::GetPeername +*********************************/ + +bool ConnectionDescriptor::GetPeername (struct sockaddr *s) +{ + bool ok = false; + if (s) { + socklen_t len = sizeof(*s); + int gp = getpeername (GetSocket(), s, &len); + if (gp == 0) + ok = true; + } + return ok; +} + +/********************************* +ConnectionDescriptor::GetSockname +*********************************/ + +bool ConnectionDescriptor::GetSockname (struct sockaddr *s) +{ + bool ok = false; + if (s) { + socklen_t len = sizeof(*s); + int gp = getsockname (GetSocket(), s, &len); + if (gp == 0) + ok = true; + } + return ok; +} + + +/********************************************** +ConnectionDescriptor::GetCommInactivityTimeout +**********************************************/ + +int ConnectionDescriptor::GetCommInactivityTimeout (int *value) +{ + if (value) { + *value = InactivityTimeout; + return 1; + } + else { + // TODO, extended logging, got bad parameter. + return 0; + } +} + + +/********************************************** +ConnectionDescriptor::SetCommInactivityTimeout +**********************************************/ + +int ConnectionDescriptor::SetCommInactivityTimeout (int *value) +{ + int out = 0; + + if (value) { + if ((*value==0) || (*value >= 2)) { + // Replace the value and send the old one back to the caller. + int v = *value; + *value = InactivityTimeout; + InactivityTimeout = v; + out = 1; + } + else { + // TODO, extended logging, got bad value. + } + } + else { + // TODO, extended logging, got bad parameter. + } + + return out; +} + +/******************************* +DatagramDescriptor::GetPeername +*******************************/ + +bool DatagramDescriptor::GetPeername (struct sockaddr *s) +{ + bool ok = false; + if (s) { + memset (s, 0, sizeof(struct sockaddr)); + memcpy (s, &ReturnAddress, sizeof(ReturnAddress)); + ok = true; + } + return ok; +} + +/******************************* +DatagramDescriptor::GetSockname +*******************************/ + +bool DatagramDescriptor::GetSockname (struct sockaddr *s) +{ + bool ok = false; + if (s) { + socklen_t len = sizeof(*s); + int gp = getsockname (GetSocket(), s, &len); + if (gp == 0) + ok = true; + } + return ok; +} + + + +/******************************************** +DatagramDescriptor::GetCommInactivityTimeout +********************************************/ + +int DatagramDescriptor::GetCommInactivityTimeout (int *value) +{ + if (value) { + *value = InactivityTimeout; + return 1; + } + else { + // TODO, extended logging, got bad parameter. + return 0; + } +} + +/******************************************** +DatagramDescriptor::SetCommInactivityTimeout +********************************************/ + +int DatagramDescriptor::SetCommInactivityTimeout (int *value) +{ + int out = 0; + + if (value) { + if ((*value==0) || (*value >= 2)) { + // Replace the value and send the old one back to the caller. + int v = *value; + *value = InactivityTimeout; + InactivityTimeout = v; + out = 1; + } + else { + // TODO, extended logging, got bad value. + } + } + else { + // TODO, extended logging, got bad parameter. + } + + return out; +} + +/***************************************************************************** + +$Id$ + +File: em.cpp +Date: 06Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +// THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY. +//#ifdef OS_UNIX + + +#include "project.h" + +// Keep a global variable floating around +// with the current loop time as set by the Event Machine. +// This avoids the need for frequent expensive calls to time(NULL); +time_t gCurrentLoopTime; + +#ifdef OS_WIN32 +unsigned gTickCountTickover; +unsigned gLastTickCount; +#endif + + +/* The numer of max outstanding timers was once a const enum defined in em.h. + * Now we define it here so that users can change its value if necessary. + */ +static int MaxOutstandingTimers = 1000; + + +/* Internal helper to convert strings to internet addresses. IPv6-aware. + * Not reentrant or threadsafe, optimized for speed. + */ +static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size); + + +/*************************************** +STATIC EventMachine_t::SetMaxTimerCount +***************************************/ + +void EventMachine_t::SetMaxTimerCount (int count) +{ + /* Allow a user to increase the maximum number of outstanding timers. + * If this gets "too high" (a metric that is of course platform dependent), + * bad things will happen like performance problems and possible overuse + * of memory. + * The actual timer mechanism is very efficient so it's hard to know what + * the practical max, but 100,000 shouldn't be too problematical. + */ + if (count < 100) + count = 100; + MaxOutstandingTimers = count; +} + + + +/****************************** +EventMachine_t::EventMachine_t +******************************/ + +EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const char*, int)): + EventCallback (event_callback), + NextHeartbeatTime (0), + LoopBreakerReader (-1), + LoopBreakerWriter (-1), + bEpoll (false), + bKqueue (false), + epfd (-1) +{ + // Default time-slice is just smaller than one hundred mills. + Quantum.tv_sec = 0; + Quantum.tv_usec = 90000; + + gTerminateSignalReceived = false; + // Make sure the current loop time is sane, in case we do any initializations of + // objects before we start running. + gCurrentLoopTime = time(NULL); + + /* We initialize the network library here (only on Windows of course) + * and initialize "loop breakers." Our destructor also does some network-level + * cleanup. There's thus an implicit assumption that any given instance of EventMachine_t + * will only call ::Run once. Is that a good assumption? Should we move some of these + * inits and de-inits into ::Run? + */ + #ifdef OS_WIN32 + WSADATA w; + WSAStartup (MAKEWORD (1, 1), &w); + #endif + + _InitializeLoopBreaker(); +} + + +/******************************* +EventMachine_t::~EventMachine_t +*******************************/ + +EventMachine_t::~EventMachine_t() +{ + // Run down descriptors + size_t i; + for (i = 0; i < NewDescriptors.size(); i++) + delete NewDescriptors[i]; + for (i = 0; i < Descriptors.size(); i++) + delete Descriptors[i]; + + close (LoopBreakerReader); + close (LoopBreakerWriter); + + if (epfd != -1) + close (epfd); + if (kqfd != -1) + close (kqfd); +} + + +/************************* +EventMachine_t::_UseEpoll +*************************/ + +void EventMachine_t::_UseEpoll() +{ + /* Temporary. + * Use an internal flag to switch in epoll-based functionality until we determine + * how it should be integrated properly and the extent of the required changes. + * A permanent solution needs to allow the integration of additional technologies, + * like kqueue and Solaris's events. + */ + + #ifdef HAVE_EPOLL + bEpoll = true; + #endif +} + +/************************** +EventMachine_t::_UseKqueue +**************************/ + +void EventMachine_t::_UseKqueue() +{ + /* Temporary. + * See comments under _UseEpoll. + */ + + #ifdef HAVE_KQUEUE + bKqueue = true; + #endif +} + + +/**************************** +EventMachine_t::ScheduleHalt +****************************/ + +void EventMachine_t::ScheduleHalt() +{ + /* This is how we stop the machine. + * This can be called by clients. Signal handlers will probably + * set the global flag. + * For now this means there can only be one EventMachine ever running at a time. + * + * IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here, + * because it may be called from signal handlers invoked from code that we don't + * control. At this writing (20Sep06), EM does NOT install any signal handlers of + * its own. + * + * We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens? + * The answer is to call evma_stop_machine, which calls here, from a SIGINT handler. + */ + gTerminateSignalReceived = true; +} + + + +/******************************* +EventMachine_t::SetTimerQuantum +*******************************/ + +void EventMachine_t::SetTimerQuantum (int interval) +{ + /* We get a timer-quantum expressed in milliseconds. + * Don't set a quantum smaller than 5 or larger than 2500. + */ + + if ((interval < 5) || (interval > 2500)) + throw std::runtime_error ("invalid timer-quantum"); + + Quantum.tv_sec = interval / 1000; + Quantum.tv_usec = (interval % 1000) * 1000; +} + + +/************************************* +(STATIC) EventMachine_t::SetuidString +*************************************/ + +void EventMachine_t::SetuidString (const char *username) +{ + /* This method takes a caller-supplied username and tries to setuid + * to that user. There is no meaningful implementation (and no error) + * on Windows. On Unix, a failure to setuid the caller-supplied string + * causes a fatal abort, because presumably the program is calling here + * in order to fulfill a security requirement. If we fail silently, + * the user may continue to run with too much privilege. + * + * TODO, we need to decide on and document a way of generating C++ level errors + * that can be wrapped in documented Ruby exceptions, so users can catch + * and handle them. And distinguish it from errors that we WON'T let the Ruby + * user catch (like security-violations and resource-overallocation). + * A setuid failure here would be in the latter category. + */ + + #ifdef OS_UNIX + if (!username || !*username) + throw std::runtime_error ("setuid_string failed: no username specified"); + + struct passwd *p = getpwnam (username); + if (!p) + throw std::runtime_error ("setuid_string failed: unknown username"); + + if (setuid (p->pw_uid) != 0) + throw std::runtime_error ("setuid_string failed: no setuid"); + + // Success. + #endif +} + + +/**************************************** +(STATIC) EventMachine_t::SetRlimitNofile +****************************************/ + +int EventMachine_t::SetRlimitNofile (int nofiles) +{ + #ifdef OS_UNIX + struct rlimit rlim; + getrlimit (RLIMIT_NOFILE, &rlim); + if (nofiles >= 0) { + rlim.rlim_cur = nofiles; + if (nofiles > rlim.rlim_max) + rlim.rlim_max = nofiles; + setrlimit (RLIMIT_NOFILE, &rlim); + // ignore the error return, for now at least. + // TODO, emit an error message someday when we have proper debug levels. + } + getrlimit (RLIMIT_NOFILE, &rlim); + return rlim.rlim_cur; + #endif + + #ifdef OS_WIN32 + // No meaningful implementation on Windows. + return 0; + #endif +} + + +/********************************* +EventMachine_t::SignalLoopBreaker +*********************************/ + +void EventMachine_t::SignalLoopBreaker() +{ + #ifdef OS_UNIX + write (LoopBreakerWriter, "", 1); + #endif + #ifdef OS_WIN32 + sendto (LoopBreakerReader, "", 0, 0, (struct sockaddr*)&(LoopBreakerTarget), sizeof(LoopBreakerTarget)); + #endif +} + + +/************************************** +EventMachine_t::_InitializeLoopBreaker +**************************************/ + +void EventMachine_t::_InitializeLoopBreaker() +{ + /* A "loop-breaker" is a socket-descriptor that we can write to in order + * to break the main select loop. Primarily useful for things running on + * threads other than the main EM thread, so they can trigger processing + * of events that arise exogenously to the EM. + * Keep the loop-breaker pipe out of the main descriptor set, otherwise + * its events will get passed on to user code. + */ + + #ifdef OS_UNIX + int fd[2]; + if (pipe (fd)) + throw std::runtime_error ("no loop breaker"); + + LoopBreakerWriter = fd[1]; + LoopBreakerReader = fd[0]; + #endif + + #ifdef OS_WIN32 + int sd = socket (AF_INET, SOCK_DGRAM, 0); + if (sd == INVALID_SOCKET) + throw std::runtime_error ("no loop breaker socket"); + SetSocketNonblocking (sd); + + memset (&LoopBreakerTarget, 0, sizeof(LoopBreakerTarget)); + LoopBreakerTarget.sin_family = AF_INET; + LoopBreakerTarget.sin_addr.s_addr = inet_addr ("127.0.0.1"); + + srand ((int)time(NULL)); + int i; + for (i=0; i < 100; i++) { + int r = (rand() % 10000) + 20000; + LoopBreakerTarget.sin_port = htons (r); + if (bind (sd, (struct sockaddr*)&LoopBreakerTarget, sizeof(LoopBreakerTarget)) == 0) + break; + } + + if (i == 100) + throw std::runtime_error ("no loop breaker"); + LoopBreakerReader = sd; + #endif +} + + +/******************* +EventMachine_t::Run +*******************/ + +void EventMachine_t::Run() +{ + #ifdef OS_WIN32 + HookControlC (true); + #endif + + #ifdef HAVE_EPOLL + if (bEpoll) { + epfd = epoll_create (MaxEpollDescriptors); + if (epfd == -1) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno)); + throw std::runtime_error (buf); + } + int cloexec = fcntl (epfd, F_GETFD, 0); + assert (cloexec >= 0); + cloexec |= FD_CLOEXEC; + fcntl (epfd, F_SETFD, cloexec); + + assert (LoopBreakerReader >= 0); + LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this); + assert (ld); + Add (ld); + } + #endif + + #ifdef HAVE_KQUEUE + if (bKqueue) { + kqfd = kqueue(); + if (kqfd == -1) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno)); + throw std::runtime_error (buf); + } + // cloexec not needed. By definition, kqueues are not carried across forks. + + assert (LoopBreakerReader >= 0); + LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this); + assert (ld); + Add (ld); + } + #endif + + while (true) { + gCurrentLoopTime = time(NULL); + if (!_RunTimers()) + break; + + /* _Add must precede _Modify because the same descriptor might + * be on both lists during the same pass through the machine, + * and to modify a descriptor before adding it would fail. + */ + _AddNewDescriptors(); + _ModifyDescriptors(); + + if (!_RunOnce()) + break; + if (gTerminateSignalReceived) + break; + } + + #ifdef OS_WIN32 + HookControlC (false); + #endif +} + + +/************************ +EventMachine_t::_RunOnce +************************/ + +bool EventMachine_t::_RunOnce() +{ + if (bEpoll) + return _RunEpollOnce(); + else if (bKqueue) + return _RunKqueueOnce(); + else + return _RunSelectOnce(); +} + + + +/***************************** +EventMachine_t::_RunEpollOnce +*****************************/ + +bool EventMachine_t::_RunEpollOnce() +{ + #ifdef HAVE_EPOLL + assert (epfd != -1); + struct epoll_event ev [MaxEpollDescriptors]; + int s; + + #ifdef BUILD_FOR_RUBY + TRAP_BEG; + #endif + s = epoll_wait (epfd, ev, MaxEpollDescriptors, 50); + #ifdef BUILD_FOR_RUBY + TRAP_END; + #endif + + if (s > 0) { + for (int i=0; i < s; i++) { + EventableDescriptor *ed = (EventableDescriptor*) ev[i].data.ptr; + + if (ev[i].events & (EPOLLERR | EPOLLHUP)) + ed->ScheduleClose (false); + if (ev[i].events & EPOLLIN) + ed->Read(); + if (ev[i].events & EPOLLOUT) { + ed->Write(); + epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent()); + // Ignoring return value + } + } + } + else if (s < 0) { + // epoll_wait can fail on error in a handful of ways. + // If this happens, then wait for a little while to avoid busy-looping. + // If the error was EINTR, we probably caught SIGCHLD or something, + // so keep the wait short. + timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000}; + EmSelect (0, NULL, NULL, NULL, &tv); + } + + { // cleanup dying sockets + // vector::pop_back works in constant time. + // TODO, rip this out and only delete the descriptors we know have died, + // rather than traversing the whole list. + // Modified 05Jan08 per suggestions by Chris Heath. It's possible that + // an EventableDescriptor will have a descriptor value of -1. That will + // happen if EventableDescriptor::Close was called on it. In that case, + // don't call epoll_ctl to remove the socket's filters from the epoll set. + // According to the epoll docs, this happens automatically when the + // descriptor is closed anyway. This is different from the case where + // the socket has already been closed but the descriptor in the ED object + // hasn't yet been set to INVALID_SOCKET. + int i, j; + int nSockets = Descriptors.size(); + for (i=0, j=0; i < nSockets; i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + if (ed->ShouldDelete()) { + if (ed->GetSocket() != INVALID_SOCKET) { + assert (bEpoll); // wouldn't be in this method otherwise. + assert (epfd != -1); + int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent()); + // ENOENT or EBADF are not errors because the socket may be already closed when we get here. + if (e && (errno != ENOENT) && (errno != EBADF)) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + + ModifiedDescriptors.erase (ed); + delete ed; + } + else + Descriptors [j++] = ed; + } + while ((size_t)j < Descriptors.size()) + Descriptors.pop_back(); + + } + + // TODO, heartbeats. + // Added 14Sep07, its absence was noted by Brian Candler. But the comment was here, indicated + // that this got thought about and not done when EPOLL was originally written. Was there a reason + // not to do it, or was it an oversight? Certainly, running a heartbeat on 50,000 connections every + // two seconds can get to be a real bear, especially if all we're doing is timing out dead ones. + // Maybe there's a better way to do this. (Or maybe it's not that expensive after all.) + // + { // dispatch heartbeats + if (gCurrentLoopTime >= NextHeartbeatTime) { + NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval; + + for (int i=0; i < Descriptors.size(); i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + ed->Heartbeat(); + } + } + } + + #ifdef BUILD_FOR_RUBY + if (!rb_thread_alone()) { + rb_thread_schedule(); + } + #endif + + return true; + #else + throw std::runtime_error ("epoll is not implemented on this platform"); + #endif +} + + +/****************************** +EventMachine_t::_RunKqueueOnce +******************************/ + +bool EventMachine_t::_RunKqueueOnce() +{ + #ifdef HAVE_KQUEUE + assert (kqfd != -1); + const int maxKevents = 2000; + struct kevent Karray [maxKevents]; + struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region + + int k; + #ifdef BUILD_FOR_RUBY + TRAP_BEG; + #endif + k = kevent (kqfd, NULL, 0, Karray, maxKevents, &ts); + #ifdef BUILD_FOR_RUBY + TRAP_END; + #endif + struct kevent *ke = Karray; + while (k > 0) { + EventableDescriptor *ed = (EventableDescriptor*) (ke->udata); + assert (ed); + + if (ke->filter == EVFILT_READ) + ed->Read(); + else if (ke->filter == EVFILT_WRITE) + ed->Write(); + else + cerr << "Discarding unknown kqueue event " << ke->filter << endl; + + --k; + ++ke; + } + + { // cleanup dying sockets + // vector::pop_back works in constant time. + // TODO, rip this out and only delete the descriptors we know have died, + // rather than traversing the whole list. + // In kqueue, closing a descriptor automatically removes its event filters. + + int i, j; + int nSockets = Descriptors.size(); + for (i=0, j=0; i < nSockets; i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + if (ed->ShouldDelete()) { + ModifiedDescriptors.erase (ed); + delete ed; + } + else + Descriptors [j++] = ed; + } + while ((size_t)j < Descriptors.size()) + Descriptors.pop_back(); + + } + + { // dispatch heartbeats + if (gCurrentLoopTime >= NextHeartbeatTime) { + NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval; + + for (int i=0; i < Descriptors.size(); i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + ed->Heartbeat(); + } + } + } + + + // TODO, replace this with rb_thread_blocking_region for 1.9 builds. + #ifdef BUILD_FOR_RUBY + if (!rb_thread_alone()) { + rb_thread_schedule(); + } + #endif + + return true; + #else + throw std::runtime_error ("kqueue is not implemented on this platform"); + #endif +} + + +/********************************* +EventMachine_t::_ModifyEpollEvent +*********************************/ + +void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed) +{ + #ifdef HAVE_EPOLL + if (bEpoll) { + assert (epfd != -1); + assert (ed); + int e = epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent()); + if (e) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "unable to modify epoll event: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + #endif +} + + + +/************************** +SelectData_t::SelectData_t +**************************/ + +SelectData_t::SelectData_t() +{ + maxsocket = 0; + FD_ZERO (&fdreads); + FD_ZERO (&fdwrites); +} + + +#ifdef BUILD_FOR_RUBY +/***************** +_SelectDataSelect +*****************/ + +#ifdef HAVE_TBR +static VALUE _SelectDataSelect (void *v) +{ + SelectData_t *sd = (SelectData_t*)v; + sd->nSockets = select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), NULL, &(sd->tv)); + return Qnil; +} +#endif + +/********************* +SelectData_t::_Select +*********************/ + +int SelectData_t::_Select() +{ + #ifdef HAVE_TBR + rb_thread_blocking_region (_SelectDataSelect, (void*)this, RUBY_UBF_IO, 0); + return nSockets; + #endif + + #ifndef HAVE_TBR + return EmSelect (maxsocket+1, &fdreads, &fdwrites, NULL, &tv); + #endif +} +#endif + + + +/****************************** +EventMachine_t::_RunSelectOnce +******************************/ + +bool EventMachine_t::_RunSelectOnce() +{ + // Crank the event machine once. + // If there are no descriptors to process, then sleep + // for a few hundred mills to avoid busy-looping. + // Return T/F to indicate whether we should continue. + // This is based on a select loop. Alternately provide epoll + // if we know we're running on a 2.6 kernel. + // epoll will be effective if we provide it as an alternative, + // however it has the same problem interoperating with Ruby + // threads that select does. + + //cerr << "X"; + + /* This protection is now obsolete, because we will ALWAYS + * have at least one descriptor (the loop-breaker) to read. + */ + /* + if (Descriptors.size() == 0) { + #ifdef OS_UNIX + timeval tv = {0, 200 * 1000}; + EmSelect (0, NULL, NULL, NULL, &tv); + return true; + #endif + #ifdef OS_WIN32 + Sleep (200); + return true; + #endif + } + */ + + SelectData_t SelectData; + /* + fd_set fdreads, fdwrites; + FD_ZERO (&fdreads); + FD_ZERO (&fdwrites); + + int maxsocket = 0; + */ + + // Always read the loop-breaker reader. + // Changed 23Aug06, provisionally implemented for Windows with a UDP socket + // running on localhost with a randomly-chosen port. (*Puke*) + // Windows has a version of the Unix pipe() library function, but it doesn't + // give you back descriptors that are selectable. + FD_SET (LoopBreakerReader, &(SelectData.fdreads)); + if (SelectData.maxsocket < LoopBreakerReader) + SelectData.maxsocket = LoopBreakerReader; + + // prepare the sockets for reading and writing + size_t i; + for (i = 0; i < Descriptors.size(); i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + int sd = ed->GetSocket(); + assert (sd != INVALID_SOCKET); + + if (ed->SelectForRead()) + FD_SET (sd, &(SelectData.fdreads)); + if (ed->SelectForWrite()) + FD_SET (sd, &(SelectData.fdwrites)); + + if (SelectData.maxsocket < sd) + SelectData.maxsocket = sd; + } + + + { // read and write the sockets + //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000. + //timeval tv = Quantum; + SelectData.tv = Quantum; + int s = SelectData._Select(); + //rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0); + //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv)); + //int s = SelectData.nSockets; + if (s > 0) { + /* Changed 01Jun07. We used to handle the Loop-breaker right here. + * Now we do it AFTER all the regular descriptors. There's an + * incredibly important and subtle reason for this. Code on + * loop breakers is sometimes used to cause the reactor core to + * cycle (for example, to allow outbound network buffers to drain). + * If a loop-breaker handler reschedules itself (say, after determining + * that the write buffers are still too full), then it will execute + * IMMEDIATELY if _ReadLoopBreaker is done here instead of after + * the other descriptors are processed. That defeats the whole purpose. + */ + for (i=0; i < Descriptors.size(); i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + int sd = ed->GetSocket(); + assert (sd != INVALID_SOCKET); + + if (FD_ISSET (sd, &(SelectData.fdwrites))) + ed->Write(); + if (FD_ISSET (sd, &(SelectData.fdreads))) + ed->Read(); + } + + if (FD_ISSET (LoopBreakerReader, &(SelectData.fdreads))) + _ReadLoopBreaker(); + } + else if (s < 0) { + // select can fail on error in a handful of ways. + // If this happens, then wait for a little while to avoid busy-looping. + // If the error was EINTR, we probably caught SIGCHLD or something, + // so keep the wait short. + timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000}; + EmSelect (0, NULL, NULL, NULL, &tv); + } + } + + + { // dispatch heartbeats + if (gCurrentLoopTime >= NextHeartbeatTime) { + NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval; + + for (i=0; i < Descriptors.size(); i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + ed->Heartbeat(); + } + } + } + + { // cleanup dying sockets + // vector::pop_back works in constant time. + int i, j; + int nSockets = Descriptors.size(); + for (i=0, j=0; i < nSockets; i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + if (ed->ShouldDelete()) + delete ed; + else + Descriptors [j++] = ed; + } + while ((size_t)j < Descriptors.size()) + Descriptors.pop_back(); + + } + + return true; +} + + +/******************************** +EventMachine_t::_ReadLoopBreaker +********************************/ + +void EventMachine_t::_ReadLoopBreaker() +{ + /* The loop breaker has selected readable. + * Read it ONCE (it may block if we try to read it twice) + * and send a loop-break event back to user code. + */ + char buffer [1024]; + read (LoopBreakerReader, buffer, sizeof(buffer)); + if (EventCallback) + (*EventCallback)("", EM_LOOPBREAK_SIGNAL, "", 0); +} + + +/************************** +EventMachine_t::_RunTimers +**************************/ + +bool EventMachine_t::_RunTimers() +{ + // These are caller-defined timer handlers. + // Return T/F to indicate whether we should continue the main loop. + // We rely on the fact that multimaps sort by their keys to avoid + // inspecting the whole list every time we come here. + // Just keep inspecting and processing the list head until we hit + // one that hasn't expired yet. + + #ifdef OS_UNIX + struct timeval tv; + gettimeofday (&tv, NULL); + Int64 now = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec)); + #endif + + #ifdef OS_WIN32 + unsigned tick = GetTickCount(); + if (tick < gLastTickCount) + gTickCountTickover += 1; + gLastTickCount = tick; + Int64 now = ((Int64)gTickCountTickover << 32) + (Int64)tick; + #endif + + while (true) { + multimap<Int64,Timer_t>::iterator i = Timers.begin(); + if (i == Timers.end()) + break; + if (i->first > now) + break; + if (EventCallback) + (*EventCallback) ("", EM_TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length()); + Timers.erase (i); + } + return true; +} + + + +/*********************************** +EventMachine_t::InstallOneshotTimer +***********************************/ + +const char *EventMachine_t::InstallOneshotTimer (int milliseconds) +{ + if (Timers.size() > MaxOutstandingTimers) + return false; + // Don't use the global loop-time variable here, because we might + // get called before the main event machine is running. + + #ifdef OS_UNIX + struct timeval tv; + gettimeofday (&tv, NULL); + Int64 fire_at = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec)); + fire_at += ((Int64)milliseconds) * 1000LL; + #endif + + #ifdef OS_WIN32 + unsigned tick = GetTickCount(); + if (tick < gLastTickCount) + gTickCountTickover += 1; + gLastTickCount = tick; + + Int64 fire_at = ((Int64)gTickCountTickover << 32) + (Int64)tick; + fire_at += (Int64)milliseconds; + #endif + + Timer_t t; + multimap<Int64,Timer_t>::iterator i = + Timers.insert (make_pair (fire_at, t)); + return i->second.GetBindingChars(); +} + + +/******************************* +EventMachine_t::ConnectToServer +*******************************/ + +const char *EventMachine_t::ConnectToServer (const char *server, int port) +{ + /* We want to spend no more than a few seconds waiting for a connection + * to a remote host. So we use a nonblocking connect. + * Linux disobeys the usual rules for nonblocking connects. + * Per Stevens (UNP p.410), you expect a nonblocking connect to select + * both readable and writable on error, and not to return EINPROGRESS + * if the connect can be fulfilled immediately. Linux violates both + * of these expectations. + * Any kind of nonblocking connect on Linux returns EINPROGRESS. + * The socket will then return writable when the disposition of the + * connect is known, but it will not also be readable in case of + * error! Weirdly, it will be readable in case there is data to read!!! + * (Which can happen with protocols like SSH and SMTP.) + * I suppose if you were so inclined you could consider this logical, + * but it's not the way Unix has historically done it. + * So we ignore the readable flag and read getsockopt to see if there + * was an error connecting. A select timeout works as expected. + * In regard to getsockopt: Linux does the Berkeley-style thing, + * not the Solaris-style, and returns zero with the error code in + * the error parameter. + * Return the binding-text of the newly-created pending connection, + * or NULL if there was a problem. + */ + + if (!server || !*server || !port) + return NULL; + + int family, bind_size; + struct sockaddr *bind_as = name2address (server, port, &family, &bind_size); + if (!bind_as) + return NULL; + + int sd = socket (family, SOCK_STREAM, 0); + if (sd == INVALID_SOCKET) + return NULL; + + /* + sockaddr_in pin; + unsigned long HostAddr; + + HostAddr = inet_addr (server); + if (HostAddr == INADDR_NONE) { + hostent *hp = gethostbyname ((char*)server); // Windows requires (char*) + if (!hp) { + // TODO: This gives the caller a fatal error. Not good. + // They can respond by catching RuntimeError (blecch). + // Possibly we need to fire an unbind event and provide + // a status code so user code can detect the cause of the + // failure. + return NULL; + } + HostAddr = ((in_addr*)(hp->h_addr))->s_addr; + } + + memset (&pin, 0, sizeof(pin)); + pin.sin_family = AF_INET; + pin.sin_addr.s_addr = HostAddr; + pin.sin_port = htons (port); + + int sd = socket (AF_INET, SOCK_STREAM, 0); + if (sd == INVALID_SOCKET) + return NULL; + */ + + // From here on, ALL error returns must close the socket. + // Set the new socket nonblocking. + if (!SetSocketNonblocking (sd)) { + closesocket (sd); + return NULL; + } + // Disable slow-start (Nagle algorithm). + int one = 1; + setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one)); + + const char *out = NULL; + + #ifdef OS_UNIX + //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { + if (connect (sd, bind_as, bind_size) == 0) { + // This is a connect success, which Linux appears + // never to give when the socket is nonblocking, + // even if the connection is intramachine or to + // localhost. + + /* Changed this branch 08Aug06. Evidently some kernels + * (FreeBSD for example) will actually return success from + * a nonblocking connect. This is a pretty simple case, + * just set up the new connection and clear the pending flag. + * Thanks to Chris Ochs for helping track this down. + * This branch never gets taken on Linux or (oddly) OSX. + * The original behavior was to throw an unimplemented, + * which the user saw as a fatal exception. Very unfriendly. + * + * Tweaked 10Aug06. Even though the connect disposition is + * known, we still set the connect-pending flag. That way + * some needed initialization will happen in the ConnectionDescriptor. + * (To wit, the ConnectionCompleted event gets sent to the client.) + */ + ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); + if (!cd) + throw std::runtime_error ("no connection allocated"); + cd->SetConnectPending (true); + Add (cd); + out = cd->GetBinding().c_str(); + } + else if (errno == EINPROGRESS) { + // Errno will generally always be EINPROGRESS, but on Linux + // we have to look at getsockopt to be sure what really happened. + int error; + socklen_t len; + len = sizeof(error); + int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len); + if ((o == 0) && (error == 0)) { + // Here, there's no disposition. + // Put the connection on the stack and wait for it to complete + // or time out. + ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); + if (!cd) + throw std::runtime_error ("no connection allocated"); + cd->SetConnectPending (true); + Add (cd); + out = cd->GetBinding().c_str(); + } + else { + /* This could be connection refused or some such thing. + * We will come here on Linux if a localhost connection fails. + * Changed 16Jul06: Originally this branch was a no-op, and + * we'd drop down to the end of the method, close the socket, + * and return NULL, which would cause the caller to GET A + * FATAL EXCEPTION. Now we keep the socket around but schedule an + * immediate close on it, so the caller will get a close-event + * scheduled on it. This was only an issue for localhost connections + * to non-listening ports. We may eventually need to revise this + * revised behavior, in case it causes problems like making it hard + * for people to know that a failure occurred. + */ + ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); + if (!cd) + throw std::runtime_error ("no connection allocated"); + cd->ScheduleClose (false); + Add (cd); + out = cd->GetBinding().c_str(); + } + } + else { + // The error from connect was something other then EINPROGRESS. + } + #endif + + #ifdef OS_WIN32 + //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { + if (connect (sd, bind_as, bind_size) == 0) { + // This is a connect success, which Windows appears + // never to give when the socket is nonblocking, + // even if the connection is intramachine or to + // localhost. + throw std::runtime_error ("unimplemented"); + } + else if (WSAGetLastError() == WSAEWOULDBLOCK) { + // Here, there's no disposition. + // Windows appears not to surface refused connections or + // such stuff at this point. + // Put the connection on the stack and wait for it to complete + // or time out. + ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); + if (!cd) + throw std::runtime_error ("no connection allocated"); + cd->SetConnectPending (true); + Add (cd); + out = cd->GetBinding().c_str(); + } + else { + // The error from connect was something other then WSAEWOULDBLOCK. + } + + #endif + + if (out == NULL) + closesocket (sd); + return out; +} + +/*********************************** +EventMachine_t::ConnectToUnixServer +***********************************/ + +const char *EventMachine_t::ConnectToUnixServer (const char *server) +{ + /* Connect to a Unix-domain server, which by definition is running + * on the same host. + * There is no meaningful implementation on Windows. + * There's no need to do a nonblocking connect, since the connection + * is always local and can always be fulfilled immediately. + */ + + #ifdef OS_WIN32 + throw std::runtime_error ("unix-domain connection unavailable on this platform"); + return NULL; + #endif + + // The whole rest of this function is only compiled on Unix systems. + #ifdef OS_UNIX + + const char *out = NULL; + + if (!server || !*server) + return NULL; + + sockaddr_un pun; + memset (&pun, 0, sizeof(pun)); + pun.sun_family = AF_LOCAL; + + // You ordinarily expect the server name field to be at least 1024 bytes long, + // but on Linux it can be MUCH shorter. + if (strlen(server) >= sizeof(pun.sun_path)) + throw std::runtime_error ("unix-domain server name is too long"); + + + strcpy (pun.sun_path, server); + + int fd = socket (AF_LOCAL, SOCK_STREAM, 0); + if (fd == INVALID_SOCKET) + return NULL; + + // From here on, ALL error returns must close the socket. + // NOTE: At this point, the socket is still a blocking socket. + if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) { + closesocket (fd); + return NULL; + } + + // Set the newly-connected socket nonblocking. + if (!SetSocketNonblocking (fd)) { + closesocket (fd); + return NULL; + } + + // Set up a connection descriptor and add it to the event-machine. + // Observe, even though we know the connection status is connect-success, + // we still set the "pending" flag, so some needed initializations take + // place. + ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this); + if (!cd) + throw std::runtime_error ("no connection allocated"); + cd->SetConnectPending (true); + Add (cd); + out = cd->GetBinding().c_str(); + + if (out == NULL) + closesocket (fd); + + return out; + #endif +} + +/************************ +EventMachine_t::AttachFD +************************/ + +const char *EventMachine_t::AttachFD (int fd, bool notify_readable, bool notify_writable) +{ + #ifdef OS_UNIX + if (fcntl(fd, F_GETFL, 0) < 0) + throw std::runtime_error ("invalid file descriptor"); + #endif + + #ifdef OS_WIN32 + // TODO: add better check for invalid file descriptors (see ioctlsocket or getsockopt) + if (fd == INVALID_SOCKET) + throw std::runtime_error ("invalid file descriptor"); + #endif + + {// Check for duplicate descriptors + size_t i; + for (i = 0; i < Descriptors.size(); i++) { + EventableDescriptor *ed = Descriptors[i]; + assert (ed); + if (ed->GetSocket() == fd) + throw std::runtime_error ("adding existing descriptor"); + } + + for (i = 0; i < NewDescriptors.size(); i++) { + EventableDescriptor *ed = NewDescriptors[i]; + assert (ed); + if (ed->GetSocket() == fd) + throw std::runtime_error ("adding existing new descriptor"); + } + } + + ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this); + if (!cd) + throw std::runtime_error ("no connection allocated"); + + cd->SetConnectPending (true); + cd->SetNotifyReadable (notify_readable); + cd->SetNotifyWritable (notify_writable); + + Add (cd); + + const char *out = NULL; + out = cd->GetBinding().c_str(); + if (out == NULL) + closesocket (fd); + return out; +} + +/************************ +EventMachine_t::DetachFD +************************/ + +int EventMachine_t::DetachFD (EventableDescriptor *ed) +{ + if (!ed) + throw std::runtime_error ("detaching bad descriptor"); + + #ifdef HAVE_EPOLL + if (bEpoll) { + if (ed->GetSocket() != INVALID_SOCKET) { + assert (bEpoll); // wouldn't be in this method otherwise. + assert (epfd != -1); + int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent()); + // ENOENT or EBADF are not errors because the socket may be already closed when we get here. + if (e && (errno != ENOENT) && (errno != EBADF)) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + } + #endif + + #ifdef HAVE_KQUEUE + if (bKqueue) { + struct kevent k; + EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_DELETE, 0, 0, ed); + int t = kevent (kqfd, &k, 1, NULL, 0, NULL); + assert (t == 0); + } + #endif + + { // remove descriptor from lists + int i, j; + int nSockets = Descriptors.size(); + for (i=0, j=0; i < nSockets; i++) { + EventableDescriptor *ted = Descriptors[i]; + assert (ted); + if (ted != ed) + Descriptors [j++] = ted; + } + while ((size_t)j < Descriptors.size()) + Descriptors.pop_back(); + + ModifiedDescriptors.erase (ed); + } + + int fd = ed->GetSocket(); + + // We depend on ~EventableDescriptor not calling close() if the socket is invalid + ed->SetSocketInvalid(); + delete ed; + + return fd; +} + +/************ +name2address +************/ + +struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size) +{ + // THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed. + // Check the more-common cases first. + // Return NULL if no resolution. + + static struct sockaddr_in in4; + #ifndef __CYGWIN__ + static struct sockaddr_in6 in6; + #endif + struct hostent *hp; + + if (!server || !*server) + server = "0.0.0.0"; + + memset (&in4, 0, sizeof(in4)); + if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) { + if (family) + *family = AF_INET; + if (bind_size) + *bind_size = sizeof(in4); + in4.sin_family = AF_INET; + in4.sin_port = htons (port); + return (struct sockaddr*)&in4; + } + + #if defined(OS_UNIX) && !defined(__CYGWIN__) + memset (&in6, 0, sizeof(in6)); + if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) { + if (family) + *family = AF_INET6; + if (bind_size) + *bind_size = sizeof(in6); + in6.sin6_family = AF_INET6; + in6.sin6_port = htons (port); + return (struct sockaddr*)&in6; + } + #endif + + #ifdef OS_WIN32 + // TODO, must complete this branch. Windows doesn't have inet_pton. + // A possible approach is to make a getaddrinfo call with the supplied + // server address, constraining the hints to ipv6 and seeing if we + // get any addresses. + // For the time being, Ipv6 addresses aren't supported on Windows. + #endif + + hp = gethostbyname ((char*)server); // Windows requires the cast. + if (hp) { + in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; + if (family) + *family = AF_INET; + if (bind_size) + *bind_size = sizeof(in4); + in4.sin_family = AF_INET; + in4.sin_port = htons (port); + return (struct sockaddr*)&in4; + } + + return NULL; +} + + +/******************************* +EventMachine_t::CreateTcpServer +*******************************/ + +const char *EventMachine_t::CreateTcpServer (const char *server, int port) +{ + /* Create a TCP-acceptor (server) socket and add it to the event machine. + * Return the binding of the new acceptor to the caller. + * This binding will be referenced when the new acceptor sends events + * to indicate accepted connections. + */ + + + int family, bind_size; + struct sockaddr *bind_here = name2address (server, port, &family, &bind_size); + if (!bind_here) + return NULL; + + const char *output_binding = NULL; + + //struct sockaddr_in sin; + + int sd_accept = socket (family, SOCK_STREAM, 0); + if (sd_accept == INVALID_SOCKET) { + goto fail; + } + + /* + memset (&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons (port); + + if (server && *server) { + sin.sin_addr.s_addr = inet_addr (server); + if (sin.sin_addr.s_addr == INADDR_NONE) { + hostent *hp = gethostbyname ((char*)server); // Windows requires the cast. + if (hp == NULL) { + //__warning ("hostname not resolved: ", server); + goto fail; + } + sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; + } + } + */ + + { // set reuseaddr to improve performance on restarts. + int oval = 1; + if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) { + //__warning ("setsockopt failed while creating listener",""); + goto fail; + } + } + + { // set CLOEXEC. Only makes sense on Unix + #ifdef OS_UNIX + int cloexec = fcntl (sd_accept, F_GETFD, 0); + assert (cloexec >= 0); + cloexec |= FD_CLOEXEC; + fcntl (sd_accept, F_SETFD, cloexec); + #endif + } + + + //if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) { + if (bind (sd_accept, bind_here, bind_size)) { + //__warning ("binding failed"); + goto fail; + } + + if (listen (sd_accept, 100)) { + //__warning ("listen failed"); + goto fail; + } + + { + // Set the acceptor non-blocking. + // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop. + if (!SetSocketNonblocking (sd_accept)) { + //int val = fcntl (sd_accept, F_GETFL, 0); + //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) { + goto fail; + } + } + + { // Looking good. + AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this); + if (!ad) + throw std::runtime_error ("unable to allocate acceptor"); + Add (ad); + output_binding = ad->GetBinding().c_str(); + } + + return output_binding; + + fail: + if (sd_accept != INVALID_SOCKET) + closesocket (sd_accept); + return NULL; +} + + +/********************************** +EventMachine_t::OpenDatagramSocket +**********************************/ + +const char *EventMachine_t::OpenDatagramSocket (const char *address, int port) +{ + const char *output_binding = NULL; + + int sd = socket (AF_INET, SOCK_DGRAM, 0); + if (sd == INVALID_SOCKET) + goto fail; + // from here on, early returns must close the socket! + + + struct sockaddr_in sin; + memset (&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + + + if (address && *address) { + sin.sin_addr.s_addr = inet_addr (address); + if (sin.sin_addr.s_addr == INADDR_NONE) { + hostent *hp = gethostbyname ((char*)address); // Windows requires the cast. + if (hp == NULL) + goto fail; + sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; + } + } + else + sin.sin_addr.s_addr = htonl (INADDR_ANY); + + + // Set the new socket nonblocking. + { + if (!SetSocketNonblocking (sd)) + //int val = fcntl (sd, F_GETFL, 0); + //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) + goto fail; + } + + if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0) + goto fail; + + { // Looking good. + DatagramDescriptor *ds = new DatagramDescriptor (sd, this); + if (!ds) + throw std::runtime_error ("unable to allocate datagram-socket"); + Add (ds); + output_binding = ds->GetBinding().c_str(); + } + + return output_binding; + + fail: + if (sd != INVALID_SOCKET) + closesocket (sd); + return NULL; +} + + + +/******************* +EventMachine_t::Add +*******************/ + +void EventMachine_t::Add (EventableDescriptor *ed) +{ + if (!ed) + throw std::runtime_error ("added bad descriptor"); + ed->SetEventCallback (EventCallback); + NewDescriptors.push_back (ed); +} + + +/******************************* +EventMachine_t::ArmKqueueWriter +*******************************/ + +void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed) +{ + #ifdef HAVE_KQUEUE + if (bKqueue) { + if (!ed) + throw std::runtime_error ("added bad descriptor"); + struct kevent k; + EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, ed); + int t = kevent (kqfd, &k, 1, NULL, 0, NULL); + assert (t == 0); + } + #endif +} + +/******************************* +EventMachine_t::ArmKqueueReader +*******************************/ + +void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed) +{ + #ifdef HAVE_KQUEUE + if (bKqueue) { + if (!ed) + throw std::runtime_error ("added bad descriptor"); + struct kevent k; + EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed); + int t = kevent (kqfd, &k, 1, NULL, 0, NULL); + assert (t == 0); + } + #endif +} + +/********************************** +EventMachine_t::_AddNewDescriptors +**********************************/ + +void EventMachine_t::_AddNewDescriptors() +{ + /* Avoid adding descriptors to the main descriptor list + * while we're actually traversing the list. + * Any descriptors that are added as a result of processing timers + * or acceptors should go on a temporary queue and then added + * while we're not traversing the main list. + * Also, it (rarely) happens that a newly-created descriptor + * is immediately scheduled to close. It might be a good + * idea not to bother scheduling these for I/O but if + * we do that, we might bypass some important processing. + */ + + for (size_t i = 0; i < NewDescriptors.size(); i++) { + EventableDescriptor *ed = NewDescriptors[i]; + if (ed == NULL) + throw std::runtime_error ("adding bad descriptor"); + + #if HAVE_EPOLL + if (bEpoll) { + assert (epfd != -1); + int e = epoll_ctl (epfd, EPOLL_CTL_ADD, ed->GetSocket(), ed->GetEpollEvent()); + if (e) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "unable to add new descriptor: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + #endif + + #if HAVE_KQUEUE + /* + if (bKqueue) { + // INCOMPLETE. Some descriptors don't want to be readable. + assert (kqfd != -1); + struct kevent k; + EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed); + int t = kevent (kqfd, &k, 1, NULL, 0, NULL); + assert (t == 0); + } + */ + #endif + + Descriptors.push_back (ed); + } + NewDescriptors.clear(); +} + + +/********************************** +EventMachine_t::_ModifyDescriptors +**********************************/ + +void EventMachine_t::_ModifyDescriptors() +{ + /* For implementations which don't level check every descriptor on + * every pass through the machine, as select does. + * If we're not selecting, then descriptors need a way to signal to the + * machine that their readable or writable status has changed. + * That's what the ::Modify call is for. We do it this way to avoid + * modifying descriptors during the loop traversal, where it can easily + * happen that an object (like a UDP socket) gets data written on it by + * the application during #post_init. That would take place BEFORE the + * descriptor even gets added to the epoll descriptor, so the modify + * operation will crash messily. + * Another really messy possibility is for a descriptor to put itself + * on the Modified list, and then get deleted before we get here. + * Remember, deletes happen after the I/O traversal and before the + * next pass through here. So we have to make sure when we delete a + * descriptor to remove it from the Modified list. + */ + + #ifdef HAVE_EPOLL + if (bEpoll) { + set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin(); + while (i != ModifiedDescriptors.end()) { + assert (*i); + _ModifyEpollEvent (*i); + ++i; + } + } + #endif + + ModifiedDescriptors.clear(); +} + + +/********************** +EventMachine_t::Modify +**********************/ + +void EventMachine_t::Modify (EventableDescriptor *ed) +{ + if (!ed) + throw std::runtime_error ("modified bad descriptor"); + ModifiedDescriptors.insert (ed); +} + + +/*********************************** +EventMachine_t::_OpenFileForWriting +***********************************/ + +const char *EventMachine_t::_OpenFileForWriting (const char *filename) +{ + /* + * Return the binding-text of the newly-opened file, + * or NULL if there was a problem. + */ + + if (!filename || !*filename) + return NULL; + + int fd = open (filename, O_CREAT|O_TRUNC|O_WRONLY|O_NONBLOCK, 0644); + + FileStreamDescriptor *fsd = new FileStreamDescriptor (fd, this); + if (!fsd) + throw std::runtime_error ("no file-stream allocated"); + Add (fsd); + return fsd->GetBinding().c_str(); + +} + + +/************************************** +EventMachine_t::CreateUnixDomainServer +**************************************/ + +const char *EventMachine_t::CreateUnixDomainServer (const char *filename) +{ + /* Create a UNIX-domain acceptor (server) socket and add it to the event machine. + * Return the binding of the new acceptor to the caller. + * This binding will be referenced when the new acceptor sends events + * to indicate accepted connections. + * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS. + */ + + #ifdef OS_WIN32 + throw std::runtime_error ("unix-domain server unavailable on this platform"); + #endif + + // The whole rest of this function is only compiled on Unix systems. + #ifdef OS_UNIX + const char *output_binding = NULL; + + struct sockaddr_un s_sun; + + int sd_accept = socket (AF_LOCAL, SOCK_STREAM, 0); + if (sd_accept == INVALID_SOCKET) { + goto fail; + } + + if (!filename || !*filename) + goto fail; + unlink (filename); + + bzero (&s_sun, sizeof(s_sun)); + s_sun.sun_family = AF_LOCAL; + strncpy (s_sun.sun_path, filename, sizeof(s_sun.sun_path)-1); + + // don't bother with reuseaddr for a local socket. + + { // set CLOEXEC. Only makes sense on Unix + #ifdef OS_UNIX + int cloexec = fcntl (sd_accept, F_GETFD, 0); + assert (cloexec >= 0); + cloexec |= FD_CLOEXEC; + fcntl (sd_accept, F_SETFD, cloexec); + #endif + } + + if (bind (sd_accept, (struct sockaddr*)&s_sun, sizeof(s_sun))) { + //__warning ("binding failed"); + goto fail; + } + + if (listen (sd_accept, 100)) { + //__warning ("listen failed"); + goto fail; + } + + { + // Set the acceptor non-blocking. + // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop. + if (!SetSocketNonblocking (sd_accept)) { + //int val = fcntl (sd_accept, F_GETFL, 0); + //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) { + goto fail; + } + } + + { // Looking good. + AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this); + if (!ad) + throw std::runtime_error ("unable to allocate acceptor"); + Add (ad); + output_binding = ad->GetBinding().c_str(); + } + + return output_binding; + + fail: + if (sd_accept != INVALID_SOCKET) + closesocket (sd_accept); + return NULL; + #endif // OS_UNIX +} + + +/********************* +EventMachine_t::Popen +*********************/ +#if OBSOLETE +const char *EventMachine_t::Popen (const char *cmd, const char *mode) +{ + #ifdef OS_WIN32 + throw std::runtime_error ("popen is currently unavailable on this platform"); + #endif + + // The whole rest of this function is only compiled on Unix systems. + // Eventually we need this functionality (or a full-duplex equivalent) on Windows. + #ifdef OS_UNIX + const char *output_binding = NULL; + + FILE *fp = popen (cmd, mode); + if (!fp) + return NULL; + + // From here, all early returns must pclose the stream. + + // According to the pipe(2) manpage, descriptors returned from pipe have both + // CLOEXEC and NONBLOCK clear. Do NOT set CLOEXEC. DO set nonblocking. + if (!SetSocketNonblocking (fileno (fp))) { + pclose (fp); + return NULL; + } + + { // Looking good. + PipeDescriptor *pd = new PipeDescriptor (fp, this); + if (!pd) + throw std::runtime_error ("unable to allocate pipe"); + Add (pd); + output_binding = pd->GetBinding().c_str(); + } + + return output_binding; + #endif +} +#endif // OBSOLETE + +/************************** +EventMachine_t::Socketpair +**************************/ + +const char *EventMachine_t::Socketpair (char * const*cmd_strings) +{ + #ifdef OS_WIN32 + throw std::runtime_error ("socketpair is currently unavailable on this platform"); + #endif + + // The whole rest of this function is only compiled on Unix systems. + // Eventually we need this functionality (or a full-duplex equivalent) on Windows. + #ifdef OS_UNIX + // Make sure the incoming array of command strings is sane. + if (!cmd_strings) + return NULL; + int j; + for (j=0; j < 100 && cmd_strings[j]; j++) + ; + if ((j==0) || (j==100)) + return NULL; + + const char *output_binding = NULL; + + int sv[2]; + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0) + return NULL; + // from here, all early returns must close the pair of sockets. + + // Set the parent side of the socketpair nonblocking. + // We don't care about the child side, and most child processes will expect their + // stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out. + // Obviously DON'T set CLOEXEC. + if (!SetSocketNonblocking (sv[0])) { + close (sv[0]); + close (sv[1]); + return NULL; + } + + pid_t f = fork(); + if (f > 0) { + close (sv[1]); + PipeDescriptor *pd = new PipeDescriptor (sv[0], f, this); + if (!pd) + throw std::runtime_error ("unable to allocate pipe"); + Add (pd); + output_binding = pd->GetBinding().c_str(); + } + else if (f == 0) { + close (sv[0]); + dup2 (sv[1], STDIN_FILENO); + close (sv[1]); + dup2 (STDIN_FILENO, STDOUT_FILENO); + execvp (cmd_strings[0], cmd_strings+1); + exit (-1); // end the child process if the exec doesn't work. + } + else + throw std::runtime_error ("no fork"); + + return output_binding; + #endif +} + + +/**************************** +EventMachine_t::OpenKeyboard +****************************/ + +const char *EventMachine_t::OpenKeyboard() +{ + KeyboardDescriptor *kd = new KeyboardDescriptor (this); + if (!kd) + throw std::runtime_error ("no keyboard-object allocated"); + Add (kd); + return kd->GetBinding().c_str(); +} + + + + + +//#endif // OS_UNIX + +/*****************************************************************************
+
+$Id$
+
+File: emwin.cpp
+Date: 05May06
+
+Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+Gmail: blackhedd
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of either: 1) the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version; or 2) Ruby's License.
+
+See the file COPYING for complete licensing information.
+
+*****************************************************************************/
+
+
+// THIS ENTIRE FILE IS FOR WINDOWS BUILDS ONLY
+// INCOMPLETE AND DISABLED FOR NOW.
+#ifdef xOS_WIN32
+
+#include "project.h"
+
+
+// Keep a global variable floating around
+// with the current loop time as set by the Event Machine.
+// This avoids the need for frequent expensive calls to time(NULL);
+time_t gCurrentLoopTime;
+
+
+/******************************
+EventMachine_t::EventMachine_t
+******************************/
+
+EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const char*, int)):
+ EventCallback (event_callback),
+ NextHeartbeatTime (0)
+{
+ gTerminateSignalReceived = false;
+ Iocp = NULL;
+}
+
+
+/*******************************
+EventMachine_t::~EventMachine_t
+*******************************/
+
+EventMachine_t::~EventMachine_t()
+{
+ cerr << "EM __dt\n";
+ if (Iocp)
+ CloseHandle (Iocp);
+}
+
+
+/****************************
+EventMachine_t::ScheduleHalt
+****************************/
+
+void EventMachine_t::ScheduleHalt()
+{
+ /* This is how we stop the machine.
+ * This can be called by clients. Signal handlers will probably
+ * set the global flag.
+ * For now this means there can only be one EventMachine ever running at a time.
+ */
+ gTerminateSignalReceived = true;
+}
+
+
+
+/*******************
+EventMachine_t::Run
+*******************/
+
+void EventMachine_t::Run()
+{
+ HookControlC (true);
+
+ Iocp = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (Iocp == NULL)
+ throw std::runtime_error ("no completion port");
+
+
+ DWORD nBytes, nCompletionKey;
+ LPOVERLAPPED Overlapped;
+
+ do {
+ gCurrentLoopTime = time(NULL);
+ // Have some kind of strategy that will dequeue maybe up to 10 completions
+ // without running the timers as long as they are available immediately.
+ // Otherwise in a busy server we're calling them every time through the loop.
+ if (!_RunTimers())
+ break;
+ if (GetQueuedCompletionStatus (Iocp, &nBytes, &nCompletionKey, &Overlapped, 1000)) {
+ }
+ cerr << "+";
+ } while (!gTerminateSignalReceived);
+
+
+ /*
+ while (true) {
+ gCurrentLoopTime = time(NULL);
+ if (!_RunTimers())
+ break;
+ _AddNewDescriptors();
+ if (!_RunOnce())
+ break;
+ if (gTerminateSignalReceived)
+ break;
+ }
+ */
+
+ HookControlC (false);
+}
+
+
+/**************************
+EventMachine_t::_RunTimers
+**************************/
+
+bool EventMachine_t::_RunTimers()
+{
+ // These are caller-defined timer handlers.
+ // Return T/F to indicate whether we should continue the main loop.
+ // We rely on the fact that multimaps sort by their keys to avoid
+ // inspecting the whole list every time we come here.
+ // Just keep inspecting and processing the list head until we hit
+ // one that hasn't expired yet.
+
+ while (true) {
+ multimap<time_t,Timer_t>::iterator i = Timers.begin();
+ if (i == Timers.end())
+ break;
+ if (i->first > gCurrentLoopTime)
+ break;
+ if (EventCallback)
+ (*EventCallback) ("", EM_TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
+ Timers.erase (i);
+ }
+ return true;
+}
+
+
+/***********************************
+EventMachine_t::InstallOneshotTimer
+***********************************/
+
+const char *EventMachine_t::InstallOneshotTimer (int seconds)
+{
+ if (Timers.size() > MaxOutstandingTimers)
+ return false;
+ // Don't use the global loop-time variable here, because we might
+ // get called before the main event machine is running.
+
+ Timer_t t;
+ Timers.insert (make_pair (time(NULL) + seconds, t));
+ return t.GetBinding().c_str();
+}
+
+
+/**********************************
+EventMachine_t::OpenDatagramSocket
+**********************************/
+
+const char *EventMachine_t::OpenDatagramSocket (const char *address, int port)
+{
+ cerr << "OPEN DATAGRAM SOCKET\n";
+ return "Unimplemented";
+}
+
+
+/*******************************
+EventMachine_t::CreateTcpServer
+*******************************/
+
+const char *EventMachine_t::CreateTcpServer (const char *server, int port)
+{
+ /* Create a TCP-acceptor (server) socket and add it to the event machine.
+ * Return the binding of the new acceptor to the caller.
+ * This binding will be referenced when the new acceptor sends events
+ * to indicate accepted connections.
+ */
+
+ const char *output_binding = NULL;
+
+ struct sockaddr_in sin;
+
+ SOCKET sd_accept = socket (AF_INET, SOCK_STREAM, 0);
+ if (sd_accept == INVALID_SOCKET) {
+ goto fail;
+ }
+
+ memset (&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons (port);
+
+ if (server && *server) {
+ sin.sin_addr.s_addr = inet_addr (server);
+ if (sin.sin_addr.s_addr == INADDR_NONE) {
+ hostent *hp = gethostbyname (server);
+ if (hp == NULL) {
+ //__warning ("hostname not resolved: ", server);
+ goto fail;
+ }
+ sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
+ }
+ }
+
+
+ // No need to set reuseaddr on Windows.
+
+
+ if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
+ //__warning ("binding failed");
+ goto fail;
+ }
+
+ if (listen (sd_accept, 100)) {
+ //__warning ("listen failed");
+ goto fail;
+ }
+
+ { // Looking good.
+ AcceptorDescriptor *ad = new AcceptorDescriptor (this, sd_accept);
+ if (!ad)
+ throw std::runtime_error ("unable to allocate acceptor");
+ Add (ad);
+ output_binding = ad->GetBinding().c_str();
+
+ CreateIoCompletionPort ((HANDLE)sd_accept, Iocp, NULL, 0);
+ SOCKET sd = socket (AF_INET, SOCK_STREAM, 0);
+ CreateIoCompletionPort ((HANDLE)sd, Iocp, NULL, 0);
+ AcceptEx (sd_accept, sd,
+ }
+
+ return output_binding;
+
+ fail:
+ if (sd_accept != INVALID_SOCKET)
+ closesocket (sd_accept);
+ return NULL;
+}
+
+
+/*******************************
+EventMachine_t::ConnectToServer
+*******************************/
+
+const char *EventMachine_t::ConnectToServer (const char *server, int port)
+{
+ if (!server || !*server || !port)
+ return NULL;
+
+ sockaddr_in pin;
+ unsigned long HostAddr;
+
+ HostAddr = inet_addr (server);
+ if (HostAddr == INADDR_NONE) {
+ hostent *hp = gethostbyname (server);
+ if (!hp)
+ return NULL;
+ HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
+ }
+
+ memset (&pin, 0, sizeof(pin));
+ pin.sin_family = AF_INET;
+ pin.sin_addr.s_addr = HostAddr;
+ pin.sin_port = htons (port);
+
+ int sd = socket (AF_INET, SOCK_STREAM, 0);
+ if (sd == INVALID_SOCKET)
+ return NULL;
+
+
+ LPOVERLAPPED olap = (LPOVERLAPPED) calloc (1, sizeof (OVERLAPPED));
+ cerr << "I'm dying now\n";
+ throw runtime_error ("UNIMPLEMENTED!!!\n");
+
+}
+
+
+
+/*******************
+EventMachine_t::Add
+*******************/
+
+void EventMachine_t::Add (EventableDescriptor *ed)
+{
+ cerr << "ADD\n";
+}
+
+
+
+#endif // OS_WIN32
+
+/***************************************************************************** + +$Id$ + +File: epoll.cpp +Date: 06Jun07 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + + +#ifdef HAVE_EPOLL + +#include "project.h" + +#endif // HAVE_EPOLL + +/***************************************************************************** + +$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $ + +File: mapper.cpp +Date: 02Jul07 + +Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved. +Gmail: garbagecat10 + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + + +////////////////////////////////////////////////////////////////////// +// UNIX implementation +////////////////////////////////////////////////////////////////////// + + +#ifdef OS_UNIX + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <errno.h> + +#include <iostream> +#include "unistd.h" +#include <string> +#include <cstring> +#include <stdexcept> +using namespace std; + +#include "mapper.h" + +/****************** +Mapper_t::Mapper_t +******************/ + +Mapper_t::Mapper_t (const string &filename) +{ + /* We ASSUME we can open the file. + * (More precisely, we assume someone else checked before we got here.) + */ + + Fd = open (filename.c_str(), O_RDONLY); + if (Fd < 0) + throw runtime_error (strerror (errno)); + + struct stat st; + if (fstat (Fd, &st)) + throw runtime_error (strerror (errno)); + FileSize = st.st_size; + + MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0); + if (MapPoint == MAP_FAILED) + throw runtime_error (strerror (errno)); +} + + +/******************* +Mapper_t::~Mapper_t +*******************/ + +Mapper_t::~Mapper_t() +{ + Close(); +} + + +/*************** +Mapper_t::Close +***************/ + +void Mapper_t::Close() +{ + // Can be called multiple times. + // Calls to GetChunk are invalid after a call to Close. + if (MapPoint) { + munmap ((void*)MapPoint, FileSize); + MapPoint = NULL; + } + if (Fd >= 0) { + close (Fd); + Fd = -1; + } +} + +/****************** +Mapper_t::GetChunk +******************/ + +const char *Mapper_t::GetChunk (unsigned start) +{ + return MapPoint + start; +} + + + +#endif // OS_UNIX + + +////////////////////////////////////////////////////////////////////// +// WINDOWS implementation +////////////////////////////////////////////////////////////////////// + +#ifdef OS_WIN32 + +#include <windows.h> + +#include <iostream> +#include <string> +#include <stdexcept> +using namespace std; + +#include "mapper.h" + +/****************** +Mapper_t::Mapper_t +******************/ + +Mapper_t::Mapper_t (const string &filename) +{ + /* We ASSUME we can open the file. + * (More precisely, we assume someone else checked before we got here.) + */ + + hFile = INVALID_HANDLE_VALUE; + hMapping = NULL; + MapPoint = NULL; + FileSize = 0; + + hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile == INVALID_HANDLE_VALUE) + throw runtime_error ("File not found"); + + BY_HANDLE_FILE_INFORMATION i; + if (GetFileInformationByHandle (hFile, &i)) + FileSize = i.nFileSizeLow; + + hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL); + if (!hMapping) + throw runtime_error ("File not mapped"); + + MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0); + if (!MapPoint) + throw runtime_error ("Mappoint not read"); +} + + +/******************* +Mapper_t::~Mapper_t +*******************/ + +Mapper_t::~Mapper_t() +{ + Close(); +} + +/*************** +Mapper_t::Close +***************/ + +void Mapper_t::Close() +{ + // Can be called multiple times. + // Calls to GetChunk are invalid after a call to Close. + if (MapPoint) { + UnmapViewOfFile (MapPoint); + MapPoint = NULL; + } + if (hMapping != NULL) { + CloseHandle (hMapping); + hMapping = NULL; + } + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle (hFile); + hMapping = INVALID_HANDLE_VALUE; + } +} + + +/****************** +Mapper_t::GetChunk +******************/ + +const char *Mapper_t::GetChunk (unsigned start) +{ + return MapPoint + start; +} + + + +#endif // OS_WINDOWS +/***************************************************************************** + +$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $ + +File: rubymain.cpp +Date: 02Jul07 + +Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved. +Gmail: garbagecat10 + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + + + +#include <iostream> +#include <stdexcept> +using namespace std; + +#include <ruby.h> +#include "mapper.h" + +static VALUE EmModule; +static VALUE FastFileReader; +static VALUE Mapper; + + + +/********* +mapper_dt +*********/ + +static void mapper_dt (void *ptr) +{ + if (ptr) + delete (Mapper_t*) ptr; +} + +/********** +mapper_new +**********/ + +static VALUE mapper_new (VALUE self, VALUE filename) +{ + Mapper_t *m = new Mapper_t (StringValuePtr (filename)); + if (!m) + rb_raise (rb_eException, "No Mapper Object"); + VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m); + return v; +} + + +/**************** +mapper_get_chunk +****************/ + +static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length) +{ + Mapper_t *m = NULL; + Data_Get_Struct (self, Mapper_t, m); + if (!m) + rb_raise (rb_eException, "No Mapper Object"); + + // TODO, what if some moron sends us a negative start value? + unsigned _start = NUM2INT (start); + unsigned _length = NUM2INT (length); + if ((_start + _length) > m->GetFileSize()) + rb_raise (rb_eException, "Mapper Range Error"); + + const char *chunk = m->GetChunk (_start); + if (!chunk) + rb_raise (rb_eException, "No Mapper Chunk"); + return rb_str_new (chunk, _length); +} + +/************ +mapper_close +************/ + +static VALUE mapper_close (VALUE self) +{ + Mapper_t *m = NULL; + Data_Get_Struct (self, Mapper_t, m); + if (!m) + rb_raise (rb_eException, "No Mapper Object"); + m->Close(); + return Qnil; +} + +/*********** +mapper_size +***********/ + +static VALUE mapper_size (VALUE self) +{ + Mapper_t *m = NULL; + Data_Get_Struct (self, Mapper_t, m); + if (!m) + rb_raise (rb_eException, "No Mapper Object"); + return INT2NUM (m->GetFileSize()); +} + + +/********************** +Init_fastfilereaderext +**********************/ + +extern "C" void Init_fastfilereaderext() +{ + EmModule = rb_define_module ("EventMachine"); + FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject); + Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject); + + rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1); + rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0); + rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0); + rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2); +} + + + +/***************************************************************************** + +$Id$ + +File: files.cpp +Date: 26Aug06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + + +/****************************************** +FileStreamDescriptor::FileStreamDescriptor +******************************************/ + +FileStreamDescriptor::FileStreamDescriptor (int fd, EventMachine_t *em): + EventableDescriptor (fd, em), + OutboundDataSize (0) +{ +cerr << "#####"; +} + + +/******************************************* +FileStreamDescriptor::~FileStreamDescriptor +*******************************************/ + +FileStreamDescriptor::~FileStreamDescriptor() +{ + // Run down any stranded outbound data. + for (size_t i=0; i < OutboundPages.size(); i++) + OutboundPages[i].Free(); +} + + +/************************** +FileStreamDescriptor::Read +**************************/ + +void FileStreamDescriptor::Read() +{ +} + +/*************************** +FileStreamDescriptor::Write +***************************/ + +void FileStreamDescriptor::Write() +{ +} + + +/******************************* +FileStreamDescriptor::Heartbeat +*******************************/ + +void FileStreamDescriptor::Heartbeat() +{ +} + + +/*********************************** +FileStreamDescriptor::SelectForRead +***********************************/ + +bool FileStreamDescriptor::SelectForRead() +{ + cerr << "R?"; + return false; +} + + +/************************************ +FileStreamDescriptor::SelectForWrite +************************************/ + +bool FileStreamDescriptor::SelectForWrite() +{ + cerr << "W?"; + return false; +} + + +/***************************************************************************** + +$Id$ + +File: kb.cpp +Date: 24Aug07 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + + +/************************************** +KeyboardDescriptor::KeyboardDescriptor +**************************************/ + +KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em): + EventableDescriptor (0, parent_em), + bReadAttemptedAfterClose (false), + LastIo (gCurrentLoopTime), + InactivityTimeout (0) +{ + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueReader (this); + #endif +} + + +/*************************************** +KeyboardDescriptor::~KeyboardDescriptor +***************************************/ + +KeyboardDescriptor::~KeyboardDescriptor() +{ +} + + +/************************* +KeyboardDescriptor::Write +*************************/ + +void KeyboardDescriptor::Write() +{ + // Why are we here? + throw std::runtime_error ("bad code path in keyboard handler"); +} + + +/***************************** +KeyboardDescriptor::Heartbeat +*****************************/ + +void KeyboardDescriptor::Heartbeat() +{ + // no-op +} + + +/************************ +KeyboardDescriptor::Read +************************/ + +void KeyboardDescriptor::Read() +{ + char c; + read (GetSocket(), &c, 1); + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, &c, 1); +} + + + + +#if 0 +/****************************** +PipeDescriptor::PipeDescriptor +******************************/ + +PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em): + EventableDescriptor (fd, parent_em), + bReadAttemptedAfterClose (false), + LastIo (gCurrentLoopTime), + InactivityTimeout (0), + OutboundDataSize (0), + SubprocessPid (subpid) +{ + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif +} + + +/******************************* +PipeDescriptor::~PipeDescriptor +*******************************/ + +PipeDescriptor::~PipeDescriptor() +{ + // Run down any stranded outbound data. + for (size_t i=0; i < OutboundPages.size(); i++) + OutboundPages[i].Free(); + + /* As a virtual destructor, we come here before the base-class + * destructor that closes our file-descriptor. + * We have to make sure the subprocess goes down (if it's not + * already down) and we have to reap the zombie. + * + * This implementation is PROVISIONAL and will surely be improved. + * The intention here is that we never block, hence the highly + * undesirable sleeps. But if we can't reap the subprocess even + * after sending it SIGKILL, then something is wrong and we + * throw a fatal exception, which is also not something we should + * be doing. + * + * Eventually the right thing to do will be to have the reactor + * core respond to SIGCHLD by chaining a handler on top of the + * one Ruby may have installed, and dealing with a list of dead + * children that are pending cleanup. + * + * Since we want to have a signal processor integrated into the + * client-visible API, let's wait until that is done before cleaning + * this up. + */ + + struct timespec req = {0, 10000000}; + kill (SubprocessPid, SIGTERM); + nanosleep (&req, NULL); + if (waitpid (SubprocessPid, NULL, WNOHANG) == 0) { + kill (SubprocessPid, SIGKILL); + nanosleep (&req, NULL); + if (waitpid (SubprocessPid, NULL, WNOHANG) == 0) + throw std::runtime_error ("unable to reap subprocess"); + } +} + + + +/******************** +PipeDescriptor::Read +********************/ + +void PipeDescriptor::Read() +{ + int sd = GetSocket(); + if (sd == INVALID_SOCKET) { + assert (!bReadAttemptedAfterClose); + bReadAttemptedAfterClose = true; + return; + } + + LastIo = gCurrentLoopTime; + + int total_bytes_read = 0; + char readbuffer [16 * 1024]; + + for (int i=0; i < 10; i++) { + // Don't read just one buffer and then move on. This is faster + // if there is a lot of incoming. + // But don't read indefinitely. Give other sockets a chance to run. + // NOTICE, we're reading one less than the buffer size. + // That's so we can put a guard byte at the end of what we send + // to user code. + // Use read instead of recv, which on Linux gives a "socket operation + // on nonsocket" error. + + + int r = read (sd, readbuffer, sizeof(readbuffer) - 1); + //cerr << "<R:" << r << ">"; + + if (r > 0) { + total_bytes_read += r; + LastRead = gCurrentLoopTime; + + // Add a null-terminator at the the end of the buffer + // that we will send to the callback. + // DO NOT EVER CHANGE THIS. We want to explicitly allow users + // to be able to depend on this behavior, so they will have + // the option to do some things faster. Additionally it's + // a security guard against buffer overflows. + readbuffer [r] = 0; + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r); + } + else if (r == 0) { + break; + } + else { + // Basically a would-block, meaning we've read everything there is to read. + break; + } + + } + + + if (total_bytes_read == 0) { + // If we read no data on a socket that selected readable, + // it generally means the other end closed the connection gracefully. + ScheduleClose (false); + //bCloseNow = true; + } + +} + +/********************* +PipeDescriptor::Write +*********************/ + +void PipeDescriptor::Write() +{ + int sd = GetSocket(); + assert (sd != INVALID_SOCKET); + + LastIo = gCurrentLoopTime; + char output_buffer [16 * 1024]; + size_t nbytes = 0; + + while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) { + OutboundPage *op = &(OutboundPages[0]); + if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) { + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset); + nbytes += (op->Length - op->Offset); + op->Free(); + OutboundPages.pop_front(); + } + else { + int len = sizeof(output_buffer) - nbytes; + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len); + op->Offset += len; + nbytes += len; + } + } + + // We should never have gotten here if there were no data to write, + // so assert that as a sanity check. + // Don't bother to make sure nbytes is less than output_buffer because + // if it were we probably would have crashed already. + assert (nbytes > 0); + + assert (GetSocket() != INVALID_SOCKET); + int bytes_written = write (GetSocket(), output_buffer, nbytes); + + if (bytes_written > 0) { + OutboundDataSize -= bytes_written; + if ((size_t)bytes_written < nbytes) { + int len = nbytes - bytes_written; + char *buffer = (char*) malloc (len + 1); + if (!buffer) + throw std::runtime_error ("bad alloc throwing back data"); + memcpy (buffer, output_buffer + bytes_written, len); + buffer [len] = 0; + OutboundPages.push_front (OutboundPage (buffer, len)); + } + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + } + else { + #ifdef OS_UNIX + if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) + #endif + #ifdef OS_WIN32 + if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) + #endif + Close(); + } +} + + +/************************* +PipeDescriptor::Heartbeat +*************************/ + +void PipeDescriptor::Heartbeat() +{ + // If an inactivity timeout is defined, then check for it. + if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) + ScheduleClose (false); + //bCloseNow = true; +} + + +/***************************** +PipeDescriptor::SelectForRead +*****************************/ + +bool PipeDescriptor::SelectForRead() +{ + /* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */ + return true; +} + +/****************************** +PipeDescriptor::SelectForWrite +******************************/ + +bool PipeDescriptor::SelectForWrite() +{ + /* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */ + return (GetOutboundDataSize() > 0); +} + + + + +/******************************** +PipeDescriptor::SendOutboundData +********************************/ + +int PipeDescriptor::SendOutboundData (const char *data, int length) +{ + //if (bCloseNow || bCloseAfterWriting) + if (IsCloseScheduled()) + return 0; + + if (!data && (length > 0)) + throw std::runtime_error ("bad outbound data"); + char *buffer = (char *) malloc (length + 1); + if (!buffer) + throw std::runtime_error ("no allocation for outbound data"); + memcpy (buffer, data, length); + buffer [length] = 0; + OutboundPages.push_back (OutboundPage (buffer, length)); + OutboundDataSize += length; + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | EPOLLOUT); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + return length; +} + +/******************************** +PipeDescriptor::GetSubprocessPid +********************************/ + +bool PipeDescriptor::GetSubprocessPid (pid_t *pid) +{ + bool ok = false; + if (pid && (SubprocessPid > 0)) { + *pid = SubprocessPid; + ok = true; + } + return ok; +} + +#endif + +/***************************************************************************** + +$Id$ + +File: page.cpp +Date: 30Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + + +#include "project.h" + + +/****************** +PageList::PageList +******************/ + +PageList::PageList() +{ +} + + +/******************* +PageList::~PageList +*******************/ + +PageList::~PageList() +{ + while (HasPages()) + PopFront(); +} + + +/*************** +PageList::Front +***************/ + +void PageList::Front (const char **page, int *length) +{ + assert (page && length); + + if (HasPages()) { + Page p = Pages.front(); + *page = p.Buffer; + *length = p.Size; + } + else { + *page = NULL; + *length = 0; + } +} + + +/****************** +PageList::PopFront +******************/ + +void PageList::PopFront() +{ + if (HasPages()) { + Page p = Pages.front(); + Pages.pop_front(); + if (p.Buffer) + free ((void*)p.Buffer); + } +} + + +/****************** +PageList::HasPages +******************/ + +bool PageList::HasPages() +{ + return (Pages.size() > 0) ? true : false; +} + + +/************** +PageList::Push +**************/ + +void PageList::Push (const char *buf, int size) +{ + if (buf && (size > 0)) { + char *copy = (char*) malloc (size); + if (!copy) + throw runtime_error ("no memory in pagelist"); + memcpy (copy, buf, size); + Pages.push_back (Page (copy, size)); + } +} + + + + + +/***************************************************************************** + +$Id$ + +File: pipe.cpp +Date: 30May07 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + + +#ifdef OS_UNIX +// THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS. + +/****************************** +PipeDescriptor::PipeDescriptor +******************************/ + +PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em): + EventableDescriptor (fd, parent_em), + bReadAttemptedAfterClose (false), + LastIo (gCurrentLoopTime), + InactivityTimeout (0), + OutboundDataSize (0), + SubprocessPid (subpid) +{ + #ifdef HAVE_EPOLL + EpollEvent.events = EPOLLIN; + #endif + #ifdef HAVE_KQUEUE + MyEventMachine->ArmKqueueReader (this); + #endif +} + + +/******************************* +PipeDescriptor::~PipeDescriptor +*******************************/ + +PipeDescriptor::~PipeDescriptor() +{ + // Run down any stranded outbound data. + for (size_t i=0; i < OutboundPages.size(); i++) + OutboundPages[i].Free(); + + /* As a virtual destructor, we come here before the base-class + * destructor that closes our file-descriptor. + * We have to make sure the subprocess goes down (if it's not + * already down) and we have to reap the zombie. + * + * This implementation is PROVISIONAL and will surely be improved. + * The intention here is that we never block, hence the highly + * undesirable sleeps. But if we can't reap the subprocess even + * after sending it SIGKILL, then something is wrong and we + * throw a fatal exception, which is also not something we should + * be doing. + * + * Eventually the right thing to do will be to have the reactor + * core respond to SIGCHLD by chaining a handler on top of the + * one Ruby may have installed, and dealing with a list of dead + * children that are pending cleanup. + * + * Since we want to have a signal processor integrated into the + * client-visible API, let's wait until that is done before cleaning + * this up. + * + * Added a very ugly hack to support passing the subprocess's exit + * status to the user. It only makes logical sense for user code to access + * the subprocess exit status in the unbind callback. But unbind is called + * back during the EventableDescriptor destructor. So by that time there's + * no way to call back this object through an object binding, because it's + * already been cleaned up. We might have added a parameter to the unbind + * callback, but that would probably break a huge amount of existing code. + * So the hack-solution is to define an instance variable in the EventMachine + * object and stick the exit status in there, where it can easily be accessed + * with an accessor visible to user code. + * User code should ONLY access the exit status from within the unbind callback. + * Otherwise there's no guarantee it'll be valid. + * This hack won't make it impossible to run multiple EventMachines in a single + * process, but it will make it impossible to reliably nest unbind calls + * within other unbind calls. (Not sure if that's even possible.) + */ + + assert (MyEventMachine); + + // check if the process is already dead + if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) == 0) { + kill (SubprocessPid, SIGTERM); + // wait 0.25s for process to die + struct timespec req = {0, 250000000}; + nanosleep (&req, NULL); + if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) == 0) { + kill (SubprocessPid, SIGKILL); + // wait 0.5s for process to die + struct timespec req = {0, 500000000}; + nanosleep (&req, NULL); + if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) == 0) + throw std::runtime_error ("unable to reap subprocess"); + } + } +} + + + +/******************** +PipeDescriptor::Read +********************/ + +void PipeDescriptor::Read() +{ + int sd = GetSocket(); + if (sd == INVALID_SOCKET) { + assert (!bReadAttemptedAfterClose); + bReadAttemptedAfterClose = true; + return; + } + + LastIo = gCurrentLoopTime; + + int total_bytes_read = 0; + char readbuffer [16 * 1024]; + + for (int i=0; i < 10; i++) { + // Don't read just one buffer and then move on. This is faster + // if there is a lot of incoming. + // But don't read indefinitely. Give other sockets a chance to run. + // NOTICE, we're reading one less than the buffer size. + // That's so we can put a guard byte at the end of what we send + // to user code. + // Use read instead of recv, which on Linux gives a "socket operation + // on nonsocket" error. + + + int r = read (sd, readbuffer, sizeof(readbuffer) - 1); + //cerr << "<R:" << r << ">"; + + if (r > 0) { + total_bytes_read += r; + LastRead = gCurrentLoopTime; + + // Add a null-terminator at the the end of the buffer + // that we will send to the callback. + // DO NOT EVER CHANGE THIS. We want to explicitly allow users + // to be able to depend on this behavior, so they will have + // the option to do some things faster. Additionally it's + // a security guard against buffer overflows. + readbuffer [r] = 0; + if (EventCallback) + (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r); + } + else if (r == 0) { + break; + } + else { + // Basically a would-block, meaning we've read everything there is to read. + break; + } + + } + + + if (total_bytes_read == 0) { + // If we read no data on a socket that selected readable, + // it generally means the other end closed the connection gracefully. + ScheduleClose (false); + //bCloseNow = true; + } + +} + +/********************* +PipeDescriptor::Write +*********************/ + +void PipeDescriptor::Write() +{ + int sd = GetSocket(); + assert (sd != INVALID_SOCKET); + + LastIo = gCurrentLoopTime; + char output_buffer [16 * 1024]; + size_t nbytes = 0; + + while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) { + OutboundPage *op = &(OutboundPages[0]); + if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) { + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset); + nbytes += (op->Length - op->Offset); + op->Free(); + OutboundPages.pop_front(); + } + else { + int len = sizeof(output_buffer) - nbytes; + memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len); + op->Offset += len; + nbytes += len; + } + } + + // We should never have gotten here if there were no data to write, + // so assert that as a sanity check. + // Don't bother to make sure nbytes is less than output_buffer because + // if it were we probably would have crashed already. + assert (nbytes > 0); + + assert (GetSocket() != INVALID_SOCKET); + int bytes_written = write (GetSocket(), output_buffer, nbytes); + + if (bytes_written > 0) { + OutboundDataSize -= bytes_written; + if ((size_t)bytes_written < nbytes) { + int len = nbytes - bytes_written; + char *buffer = (char*) malloc (len + 1); + if (!buffer) + throw std::runtime_error ("bad alloc throwing back data"); + memcpy (buffer, output_buffer + bytes_written, len); + buffer [len] = 0; + OutboundPages.push_front (OutboundPage (buffer, len)); + } + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + } + else { + #ifdef OS_UNIX + if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) + #endif + #ifdef OS_WIN32 + if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) + #endif + Close(); + } +} + + +/************************* +PipeDescriptor::Heartbeat +*************************/ + +void PipeDescriptor::Heartbeat() +{ + // If an inactivity timeout is defined, then check for it. + if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout)) + ScheduleClose (false); + //bCloseNow = true; +} + + +/***************************** +PipeDescriptor::SelectForRead +*****************************/ + +bool PipeDescriptor::SelectForRead() +{ + /* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */ + return true; +} + +/****************************** +PipeDescriptor::SelectForWrite +******************************/ + +bool PipeDescriptor::SelectForWrite() +{ + /* Pipe descriptors, being local by definition, don't have + * a pending state, so this is simpler than for the + * ConnectionDescriptor object. + */ + return (GetOutboundDataSize() > 0); +} + + + + +/******************************** +PipeDescriptor::SendOutboundData +********************************/ + +int PipeDescriptor::SendOutboundData (const char *data, int length) +{ + //if (bCloseNow || bCloseAfterWriting) + if (IsCloseScheduled()) + return 0; + + if (!data && (length > 0)) + throw std::runtime_error ("bad outbound data"); + char *buffer = (char *) malloc (length + 1); + if (!buffer) + throw std::runtime_error ("no allocation for outbound data"); + memcpy (buffer, data, length); + buffer [length] = 0; + OutboundPages.push_back (OutboundPage (buffer, length)); + OutboundDataSize += length; + #ifdef HAVE_EPOLL + EpollEvent.events = (EPOLLIN | EPOLLOUT); + assert (MyEventMachine); + MyEventMachine->Modify (this); + #endif + return length; +} + +/******************************** +PipeDescriptor::GetSubprocessPid +********************************/ + +bool PipeDescriptor::GetSubprocessPid (pid_t *pid) +{ + bool ok = false; + if (pid && (SubprocessPid > 0)) { + *pid = SubprocessPid; + ok = true; + } + return ok; +} + + +#endif // OS_UNIX + +/***************************************************************************** + +$Id$ + +File: rubymain.cpp +Date: 06Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" +#include "eventmachine.h" +#include <ruby.h> + + + +/******* +Statics +*******/ + +static VALUE EmModule; +static VALUE EmConnection; + +static VALUE Intern_at_signature; +static VALUE Intern_at_timers; +static VALUE Intern_at_conns; +static VALUE Intern_event_callback; +static VALUE Intern_run_deferred_callbacks; +static VALUE Intern_delete; +static VALUE Intern_call; +static VALUE Intern_receive_data; + +static VALUE Intern_notify_readable; +static VALUE Intern_notify_writable; + +/**************** +t_event_callback +****************/ + +static void event_callback (const char *a1, int a2, const char *a3, int a4) +{ + if (a2 == EM_CONNECTION_READ) { + VALUE t = rb_ivar_get (EmModule, Intern_at_conns); + VALUE q = rb_hash_aref (t, rb_str_new2(a1)); + if (q == Qnil) + rb_raise (rb_eRuntimeError, "no connection"); + rb_funcall (q, Intern_receive_data, 1, rb_str_new (a3, a4)); + } + else if (a2 == EM_CONNECTION_NOTIFY_READABLE) { + VALUE t = rb_ivar_get (EmModule, Intern_at_conns); + VALUE q = rb_hash_aref (t, rb_str_new2(a1)); + if (q == Qnil) + rb_raise (rb_eRuntimeError, "no connection"); + rb_funcall (q, Intern_notify_readable, 0); + } + else if (a2 == EM_CONNECTION_NOTIFY_WRITABLE) { + VALUE t = rb_ivar_get (EmModule, Intern_at_conns); + VALUE q = rb_hash_aref (t, rb_str_new2(a1)); + if (q == Qnil) + rb_raise (rb_eRuntimeError, "no connection"); + rb_funcall (q, Intern_notify_writable, 0); + } + else if (a2 == EM_LOOPBREAK_SIGNAL) { + rb_funcall (EmModule, Intern_run_deferred_callbacks, 0); + } + else if (a2 == EM_TIMER_FIRED) { + VALUE t = rb_ivar_get (EmModule, Intern_at_timers); + VALUE q = rb_funcall (t, Intern_delete, 1, rb_str_new(a3, a4)); + if (q == Qnil) + rb_raise (rb_eRuntimeError, "no timer"); + rb_funcall (q, Intern_call, 0); + } + else + rb_funcall (EmModule, Intern_event_callback, 3, rb_str_new2(a1), (a2 << 1) | 1, rb_str_new(a3,a4)); +} + + + +/************************** +t_initialize_event_machine +**************************/ + +static VALUE t_initialize_event_machine (VALUE self) +{ + evma_initialize_library (event_callback); + return Qnil; +} + + + +/***************************** +t_run_machine_without_threads +*****************************/ + +static VALUE t_run_machine_without_threads (VALUE self) +{ + evma_run_machine(); + return Qnil; +} + + +/******************* +t_add_oneshot_timer +*******************/ + +static VALUE t_add_oneshot_timer (VALUE self, VALUE interval) +{ + const char *f = evma_install_oneshot_timer (FIX2INT (interval)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no timer"); + return rb_str_new2 (f); +} + + +/************** +t_start_server +**************/ + +static VALUE t_start_server (VALUE self, VALUE server, VALUE port) +{ + const char *f = evma_create_tcp_server (StringValuePtr(server), FIX2INT(port)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no acceptor"); + return rb_str_new2 (f); +} + +/************* +t_stop_server +*************/ + +static VALUE t_stop_server (VALUE self, VALUE signature) +{ + evma_stop_tcp_server (StringValuePtr (signature)); + return Qnil; +} + + +/******************* +t_start_unix_server +*******************/ + +static VALUE t_start_unix_server (VALUE self, VALUE filename) +{ + const char *f = evma_create_unix_domain_server (StringValuePtr(filename)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no unix-domain acceptor"); + return rb_str_new2 (f); +} + + + +/*********** +t_send_data +***********/ + +static VALUE t_send_data (VALUE self, VALUE signature, VALUE data, VALUE data_length) +{ + int b = evma_send_data_to_connection (StringValuePtr (signature), StringValuePtr (data), FIX2INT (data_length)); + return INT2NUM (b); +} + + +/*********** +t_start_tls +***********/ + +static VALUE t_start_tls (VALUE self, VALUE signature) +{ + evma_start_tls (StringValuePtr (signature)); + return Qnil; +} + +/*************** +t_set_tls_parms +***************/ + +static VALUE t_set_tls_parms (VALUE self, VALUE signature, VALUE privkeyfile, VALUE certchainfile) +{ + /* set_tls_parms takes a series of positional arguments for specifying such things + * as private keys and certificate chains. + * It's expected that the parameter list will grow as we add more supported features. + * ALL of these parameters are optional, and can be specified as empty or NULL strings. + */ + evma_set_tls_parms (StringValuePtr (signature), StringValuePtr (privkeyfile), StringValuePtr (certchainfile) ); + return Qnil; +} + +/************** +t_get_peername +**************/ + +static VALUE t_get_peername (VALUE self, VALUE signature) +{ + struct sockaddr s; + if (evma_get_peername (StringValuePtr (signature), &s)) { + return rb_str_new ((const char*)&s, sizeof(s)); + } + + return Qnil; +} + +/************** +t_get_sockname +**************/ + +static VALUE t_get_sockname (VALUE self, VALUE signature) +{ + struct sockaddr s; + if (evma_get_sockname (StringValuePtr (signature), &s)) { + return rb_str_new ((const char*)&s, sizeof(s)); + } + + return Qnil; +} + +/******************** +t_get_subprocess_pid +********************/ + +static VALUE t_get_subprocess_pid (VALUE self, VALUE signature) +{ + pid_t pid; + if (evma_get_subprocess_pid (StringValuePtr (signature), &pid)) { + return INT2NUM (pid); + } + + return Qnil; +} + +/*********************** +t_get_subprocess_status +***********************/ + +static VALUE t_get_subprocess_status (VALUE self, VALUE signature) +{ + int status; + if (evma_get_subprocess_status (StringValuePtr (signature), &status)) { + return INT2NUM (status); + } + + return Qnil; +} + +/***************************** +t_get_comm_inactivity_timeout +*****************************/ + +static VALUE t_get_comm_inactivity_timeout (VALUE self, VALUE signature) +{ + int timeout; + if (evma_get_comm_inactivity_timeout (StringValuePtr (signature), &timeout)) + return INT2FIX (timeout); + return Qnil; +} + +/***************************** +t_set_comm_inactivity_timeout +*****************************/ + +static VALUE t_set_comm_inactivity_timeout (VALUE self, VALUE signature, VALUE timeout) +{ + int ti = FIX2INT (timeout); + if (evma_set_comm_inactivity_timeout (StringValuePtr (signature), &ti)); + return Qtrue; + return Qnil; +} + + +/*************** +t_send_datagram +***************/ + +static VALUE t_send_datagram (VALUE self, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port) +{ + int b = evma_send_datagram (StringValuePtr (signature), StringValuePtr (data), FIX2INT (data_length), StringValuePtr(address), FIX2INT(port)); + return INT2NUM (b); +} + + +/****************** +t_close_connection +******************/ + +static VALUE t_close_connection (VALUE self, VALUE signature, VALUE after_writing) +{ + evma_close_connection (StringValuePtr (signature), ((after_writing == Qtrue) ? 1 : 0)); + return Qnil; +} + +/******************************** +t_report_connection_error_status +********************************/ + +static VALUE t_report_connection_error_status (VALUE self, VALUE signature) +{ + int b = evma_report_connection_error_status (StringValuePtr (signature)); + return INT2NUM (b); +} + + + +/**************** +t_connect_server +****************/ + +static VALUE t_connect_server (VALUE self, VALUE server, VALUE port) +{ + // Avoid FIX2INT in this case, because it doesn't deal with type errors properly. + // Specifically, if the value of port comes in as a string rather than an integer, + // NUM2INT will throw a type error, but FIX2INT will generate garbage. + + const char *f = evma_connect_to_server (StringValuePtr(server), NUM2INT(port)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no connection"); + return rb_str_new2 (f); +} + +/********************* +t_connect_unix_server +*********************/ + +static VALUE t_connect_unix_server (VALUE self, VALUE serversocket) +{ + const char *f = evma_connect_to_unix_server (StringValuePtr(serversocket)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no connection"); + return rb_str_new2 (f); +} + +/*********** +t_attach_fd +***********/ + +static VALUE t_attach_fd (VALUE self, VALUE file_descriptor, VALUE read_mode, VALUE write_mode) +{ + const char *f = evma_attach_fd (NUM2INT(file_descriptor), (read_mode == Qtrue) ? 1 : 0, (write_mode == Qtrue) ? 1 : 0); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no connection"); + return rb_str_new2 (f); +} + +/*********** +t_detach_fd +***********/ + +static VALUE t_detach_fd (VALUE self, VALUE signature) +{ + return INT2NUM(evma_detach_fd (StringValuePtr(signature))); +} + +/***************** +t_open_udp_socket +*****************/ + +static VALUE t_open_udp_socket (VALUE self, VALUE server, VALUE port) +{ + const char *f = evma_open_datagram_socket (StringValuePtr(server), FIX2INT(port)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no datagram socket"); + return rb_str_new2 (f); +} + + + +/***************** +t_release_machine +*****************/ + +static VALUE t_release_machine (VALUE self) +{ + evma_release_library(); + return Qnil; +} + + +/****** +t_stop +******/ + +static VALUE t_stop (VALUE self) +{ + evma_stop_machine(); + return Qnil; +} + +/****************** +t_signal_loopbreak +******************/ + +static VALUE t_signal_loopbreak (VALUE self) +{ + evma_signal_loopbreak(); + return Qnil; +} + +/************** +t_library_type +**************/ + +static VALUE t_library_type (VALUE self) +{ + return rb_eval_string (":extension"); +} + + + +/******************* +t_set_timer_quantum +*******************/ + +static VALUE t_set_timer_quantum (VALUE self, VALUE interval) +{ + evma_set_timer_quantum (FIX2INT (interval)); + return Qnil; +} + +/******************** +t_set_max_timer_count +********************/ + +static VALUE t_set_max_timer_count (VALUE self, VALUE ct) +{ + evma_set_max_timer_count (FIX2INT (ct)); + return Qnil; +} + +/*************** +t_setuid_string +***************/ + +static VALUE t_setuid_string (VALUE self, VALUE username) +{ + evma_setuid_string (StringValuePtr (username)); + return Qnil; +} + + + +/************* +t__write_file +*************/ + +static VALUE t__write_file (VALUE self, VALUE filename) +{ + const char *f = evma__write_file (StringValuePtr (filename)); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "file not opened"); + return rb_str_new2 (f); +} + +/************** +t_invoke_popen +**************/ + +static VALUE t_invoke_popen (VALUE self, VALUE cmd) +{ + // 1.8.7+ + #ifdef RARRAY_LEN + int len = RARRAY_LEN(cmd); + #else + int len = RARRAY (cmd)->len; + #endif + if (len > 98) + rb_raise (rb_eRuntimeError, "too many arguments to popen"); + char *strings [100]; + for (int i=0; i < len; i++) { + VALUE ix = INT2FIX (i); + VALUE s = rb_ary_aref (1, &ix, cmd); + strings[i] = StringValuePtr (s); + } + strings[len] = NULL; + + const char *f = evma_popen (strings); + if (!f || !*f) { + char *err = strerror (errno); + char buf[100]; + memset (buf, 0, sizeof(buf)); + snprintf (buf, sizeof(buf)-1, "no popen: %s", (err?err:"???")); + rb_raise (rb_eRuntimeError, buf); + } + return rb_str_new2 (f); +} + + +/*************** +t_read_keyboard +***************/ + +static VALUE t_read_keyboard (VALUE self) +{ + const char *f = evma_open_keyboard(); + if (!f || !*f) + rb_raise (rb_eRuntimeError, "no keyboard reader"); + return rb_str_new2 (f); +} + + +/******** +t__epoll +********/ + +static VALUE t__epoll (VALUE self) +{ + // Temporary. + evma__epoll(); + return Qnil; +} + +/********** +t__epoll_p +**********/ + +static VALUE t__epoll_p (VALUE self) +{ + #ifdef HAVE_EPOLL + return Qtrue; + #else + return Qfalse; + #endif +} + + +/********* +t__kqueue +*********/ + +static VALUE t__kqueue (VALUE self) +{ + // Temporary. + evma__kqueue(); + return Qnil; +} + +/*********** +t__kqueue_p +***********/ + +static VALUE t__kqueue_p (VALUE self) +{ + #ifdef HAVE_KQUEUE + return Qtrue; + #else + return Qfalse; + #endif +} + + +/**************** +t_send_file_data +****************/ + +static VALUE t_send_file_data (VALUE self, VALUE signature, VALUE filename) +{ + + /* The current implementation of evma_send_file_data_to_connection enforces a strict + * upper limit on the file size it will transmit (currently 32K). The function returns + * zero on success, -1 if the requested file exceeds its size limit, and a positive + * number for other errors. + * TODO: Positive return values are actually errno's, which is probably the wrong way to + * do this. For one thing it's ugly. For another, we can't be sure zero is never a real errno. + */ + + int b = evma_send_file_data_to_connection (StringValuePtr(signature), StringValuePtr(filename)); + if (b == -1) + rb_raise(rb_eRuntimeError, "File too large. send_file_data() supports files under 32k."); + if (b > 0) { + char *err = strerror (b); + char buf[1024]; + memset (buf, 0, sizeof(buf)); + snprintf (buf, sizeof(buf)-1, ": %s %s", StringValuePtr(filename),(err?err:"???")); + + rb_raise (rb_eIOError, buf); + } + + return INT2NUM (0); +} + + +/******************* +t_set_rlimit_nofile +*******************/ + +static VALUE t_set_rlimit_nofile (VALUE self, VALUE arg) +{ + arg = (NIL_P(arg)) ? -1 : NUM2INT (arg); + return INT2NUM (evma_set_rlimit_nofile (arg)); +} + +/*************************** +conn_get_outbound_data_size +***************************/ + +static VALUE conn_get_outbound_data_size (VALUE self) +{ + VALUE sig = rb_ivar_get (self, Intern_at_signature); + return INT2NUM (evma_get_outbound_data_size (StringValuePtr(sig))); +} + + +/****************************** +conn_associate_callback_target +******************************/ + +static VALUE conn_associate_callback_target (VALUE self, VALUE sig) +{ + // No-op for the time being. + return Qnil; +} + + +/*************** +t_get_loop_time +****************/ + +static VALUE t_get_loop_time (VALUE self) +{ + VALUE cTime = rb_path2class("Time"); + if (gCurrentLoopTime != 0) { + return rb_funcall(cTime, + rb_intern("at"), + 1, + INT2NUM(gCurrentLoopTime)); + } + return Qnil; +} + + +/********************* +Init_rubyeventmachine +*********************/ + +extern "C" void Init_rubyeventmachine() +{ + // Tuck away some symbol values so we don't have to look 'em up every time we need 'em. + Intern_at_signature = rb_intern ("@signature"); + Intern_at_timers = rb_intern ("@timers"); + Intern_at_conns = rb_intern ("@conns"); + + Intern_event_callback = rb_intern ("event_callback"); + Intern_run_deferred_callbacks = rb_intern ("run_deferred_callbacks"); + Intern_delete = rb_intern ("delete"); + Intern_call = rb_intern ("call"); + Intern_receive_data = rb_intern ("receive_data"); + + Intern_notify_readable = rb_intern ("notify_readable"); + Intern_notify_writable = rb_intern ("notify_writable"); + + // INCOMPLETE, we need to define class Connections inside module EventMachine + // run_machine and run_machine_without_threads are now identical. + // Must deprecate the without_threads variant. + EmModule = rb_define_module ("EventMachine"); + EmConnection = rb_define_class_under (EmModule, "Connection", rb_cObject); + + rb_define_class_under (EmModule, "ConnectionNotBound", rb_eException); + rb_define_class_under (EmModule, "NoHandlerForAcceptedConnection", rb_eException); + rb_define_class_under (EmModule, "UnknownTimerFired", rb_eException); + + rb_define_module_function (EmModule, "initialize_event_machine", (VALUE(*)(...))t_initialize_event_machine, 0); + rb_define_module_function (EmModule, "run_machine", (VALUE(*)(...))t_run_machine_without_threads, 0); + rb_define_module_function (EmModule, "run_machine_without_threads", (VALUE(*)(...))t_run_machine_without_threads, 0); + rb_define_module_function (EmModule, "add_oneshot_timer", (VALUE(*)(...))t_add_oneshot_timer, 1); + rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2); + rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); + rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1); + rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 3); + rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); + rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3); + rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5); + rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2); + rb_define_module_function (EmModule, "report_connection_error_status", (VALUE(*)(...))t_report_connection_error_status, 1); + rb_define_module_function (EmModule, "connect_server", (VALUE(*)(...))t_connect_server, 2); + rb_define_module_function (EmModule, "connect_unix_server", (VALUE(*)(...))t_connect_unix_server, 1); + + rb_define_module_function (EmModule, "attach_fd", (VALUE (*)(...))t_attach_fd, 3); + rb_define_module_function (EmModule, "detach_fd", (VALUE (*)(...))t_detach_fd, 1); + + rb_define_module_function (EmModule, "current_time", (VALUE(*)(...))t_get_loop_time, 0); + + rb_define_module_function (EmModule, "open_udp_socket", (VALUE(*)(...))t_open_udp_socket, 2); + rb_define_module_function (EmModule, "read_keyboard", (VALUE(*)(...))t_read_keyboard, 0); + rb_define_module_function (EmModule, "release_machine", (VALUE(*)(...))t_release_machine, 0); + rb_define_module_function (EmModule, "stop", (VALUE(*)(...))t_stop, 0); + rb_define_module_function (EmModule, "signal_loopbreak", (VALUE(*)(...))t_signal_loopbreak, 0); + rb_define_module_function (EmModule, "library_type", (VALUE(*)(...))t_library_type, 0); + rb_define_module_function (EmModule, "set_timer_quantum", (VALUE(*)(...))t_set_timer_quantum, 1); + rb_define_module_function (EmModule, "set_max_timer_count", (VALUE(*)(...))t_set_max_timer_count, 1); + rb_define_module_function (EmModule, "setuid_string", (VALUE(*)(...))t_setuid_string, 1); + rb_define_module_function (EmModule, "invoke_popen", (VALUE(*)(...))t_invoke_popen, 1); + rb_define_module_function (EmModule, "send_file_data", (VALUE(*)(...))t_send_file_data, 2); + + // Provisional: + rb_define_module_function (EmModule, "_write_file", (VALUE(*)(...))t__write_file, 1); + + rb_define_module_function (EmModule, "get_peername", (VALUE(*)(...))t_get_peername, 1); + rb_define_module_function (EmModule, "get_sockname", (VALUE(*)(...))t_get_sockname, 1); + rb_define_module_function (EmModule, "get_subprocess_pid", (VALUE(*)(...))t_get_subprocess_pid, 1); + rb_define_module_function (EmModule, "get_subprocess_status", (VALUE(*)(...))t_get_subprocess_status, 1); + rb_define_module_function (EmModule, "get_comm_inactivity_timeout", (VALUE(*)(...))t_get_comm_inactivity_timeout, 1); + rb_define_module_function (EmModule, "set_comm_inactivity_timeout", (VALUE(*)(...))t_set_comm_inactivity_timeout, 2); + rb_define_module_function (EmModule, "set_rlimit_nofile", (VALUE(*)(...))t_set_rlimit_nofile, 1); + + // Temporary: + rb_define_module_function (EmModule, "epoll", (VALUE(*)(...))t__epoll, 0); + rb_define_module_function (EmModule, "kqueue", (VALUE(*)(...))t__kqueue, 0); + + rb_define_module_function (EmModule, "epoll?", (VALUE(*)(...))t__epoll_p, 0); + rb_define_module_function (EmModule, "kqueue?", (VALUE(*)(...))t__kqueue_p, 0); + + rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0); + rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1); + + rb_define_const (EmModule, "TimerFired", INT2NUM(100)); + rb_define_const (EmModule, "ConnectionData", INT2NUM(101)); + rb_define_const (EmModule, "ConnectionUnbound", INT2NUM(102)); + rb_define_const (EmModule, "ConnectionAccepted", INT2NUM(103)); + rb_define_const (EmModule, "ConnectionCompleted", INT2NUM(104)); + rb_define_const (EmModule, "LoopbreakSignalled", INT2NUM(105)); + + rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(106)); + rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(107)); + +} + +/***************************************************************************** + +$Id$ + +File: sigs.cpp +Date: 06Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + +#include "project.h" + + +bool gTerminateSignalReceived; + + +/************** +SigtermHandler +**************/ + +void SigtermHandler (int sig) +{ + // This is a signal-handler, don't do anything frisky. Interrupts are disabled. + // Set the terminate flag WITHOUT trying to lock a mutex- otherwise we can easily + // self-deadlock, especially if the event machine is looping quickly. + gTerminateSignalReceived = true; +} + + +/********************* +InstallSignalHandlers +*********************/ + +void InstallSignalHandlers() +{ + #ifdef OS_UNIX + static bool bInstalled = false; + if (!bInstalled) { + bInstalled = true; + signal (SIGINT, SigtermHandler); + signal (SIGTERM, SigtermHandler); + signal (SIGPIPE, SIG_IGN); + } + #endif +} + + + +/******************* +WintelSignalHandler +*******************/ + +#ifdef OS_WIN32 +BOOL WINAPI WintelSignalHandler (DWORD control) +{ + if (control == CTRL_C_EVENT) + gTerminateSignalReceived = true; + return TRUE; +} +#endif + +/************ +HookControlC +************/ + +#ifdef OS_WIN32 +void HookControlC (bool hook) +{ + if (hook) { + // INSTALL hook + SetConsoleCtrlHandler (WintelSignalHandler, TRUE); + } + else { + // UNINSTALL hook + SetConsoleCtrlHandler (WintelSignalHandler, FALSE); + } +} +#endif + + +/***************************************************************************** + +$Id$ + +File: ssl.cpp +Date: 30Apr06 + +Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. +Gmail: blackhedd + +This program is free software; you can redistribute it and/or modify +it under the terms of either: 1) the GNU General Public License +as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version; or 2) Ruby's License. + +See the file COPYING for complete licensing information. + +*****************************************************************************/ + + +#ifdef WITH_SSL + +#include "project.h" + + +bool SslContext_t::bLibraryInitialized = false; + + + +static void InitializeDefaultCredentials(); +static EVP_PKEY *DefaultPrivateKey = NULL; +static X509 *DefaultCertificate = NULL; + +static char PrivateMaterials[] = { +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n" +"Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n" +"AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n" +"AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n" +"H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n" +"I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n" +"6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n" +"w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n" +"PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n" +"xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n" +"xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n" +"dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n" +"2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n" +"-----END RSA PRIVATE KEY-----\n" +"-----BEGIN CERTIFICATE-----\n" +"MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n" +"VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n" +"FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n" +"A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n" +"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n" +"NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n" +"EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n" +"aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n" +"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n" +"AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n" +"VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n" +"9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n" +"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n" +"HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n" +"EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n" +"VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n" +"AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n" +"aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n" +"SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n" +"Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n" +"uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n" +"-----END CERTIFICATE-----\n"}; + +/* These private materials were made with: + * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500 + * TODO: We need a full-blown capability to work with user-supplied + * keypairs and properly-signed certificates. + */ + + +/***************** +builtin_passwd_cb +*****************/ + +extern "C" int builtin_passwd_cb (char *buf, int bufsize, int rwflag, void *userdata) +{ + strcpy (buf, "kittycat"); + return 8; +} + +/**************************** +InitializeDefaultCredentials +****************************/ + +static void InitializeDefaultCredentials() +{ + BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1); + assert (bio); + + if (DefaultPrivateKey) { + // we may come here in a restart. + EVP_PKEY_free (DefaultPrivateKey); + DefaultPrivateKey = NULL; + } + PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0); + + if (DefaultCertificate) { + // we may come here in a restart. + X509_free (DefaultCertificate); + DefaultCertificate = NULL; + } + PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0); + + BIO_free (bio); +} + + + +/************************** +SslContext_t::SslContext_t +**************************/ + +SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile): + pCtx (NULL), + PrivateKey (NULL), + Certificate (NULL) +{ + /* TODO: the usage of the specified private-key and cert-chain filenames only applies to + * client-side connections at this point. Server connections currently use the default materials. + * That needs to be fixed asap. + * Also, in this implementation, server-side connections use statically defined X-509 defaults. + * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY + * objects when we call our destructor, or whether just calling SSL_CTX_free is enough. + */ + + if (!bLibraryInitialized) { + bLibraryInitialized = true; + SSL_library_init(); + OpenSSL_add_ssl_algorithms(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + ERR_load_crypto_strings(); + + InitializeDefaultCredentials(); + } + + bIsServer = is_server; + pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method()); + if (!pCtx) + throw std::runtime_error ("no SSL context"); + + SSL_CTX_set_options (pCtx, SSL_OP_ALL); + //SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3)); + + if (is_server) { + // The SSL_CTX calls here do NOT allocate memory. + int e; + if (privkeyfile.length() > 0) + e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); + else + e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey); + assert (e > 0); + if (certchainfile.length() > 0) + e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); + else + e = SSL_CTX_use_certificate (pCtx, DefaultCertificate); + assert (e > 0); + } + + SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH"); + + if (is_server) { + SSL_CTX_sess_set_cache_size (pCtx, 128); + SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12); + } + else { + int e; + if (privkeyfile.length() > 0) { + e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); + assert (e > 0); + } + if (certchainfile.length() > 0) { + e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); + assert (e > 0); + } + } +} + + + +/*************************** +SslContext_t::~SslContext_t +***************************/ + +SslContext_t::~SslContext_t() +{ + if (pCtx) + SSL_CTX_free (pCtx); + if (PrivateKey) + EVP_PKEY_free (PrivateKey); + if (Certificate) + X509_free (Certificate); +} + + + +/****************** +SslBox_t::SslBox_t +******************/ + +SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile): + bIsServer (is_server), + pSSL (NULL), + pbioRead (NULL), + pbioWrite (NULL) +{ + /* TODO someday: make it possible to re-use SSL contexts so we don't have to create + * a new one every time we come here. + */ + + Context = new SslContext_t (bIsServer, privkeyfile, certchainfile); + assert (Context); + + pbioRead = BIO_new (BIO_s_mem()); + assert (pbioRead); + + pbioWrite = BIO_new (BIO_s_mem()); + assert (pbioWrite); + + pSSL = SSL_new (Context->pCtx); + assert (pSSL); + SSL_set_bio (pSSL, pbioRead, pbioWrite); + + if (!bIsServer) + SSL_connect (pSSL); +} + + + +/******************* +SslBox_t::~SslBox_t +*******************/ + +SslBox_t::~SslBox_t() +{ + // Freeing pSSL will also free the associated BIOs, so DON'T free them separately. + if (pSSL) { + if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN) + SSL_shutdown (pSSL); + else + SSL_clear (pSSL); + SSL_free (pSSL); + } + + delete Context; +} + + + +/*********************** +SslBox_t::PutCiphertext +***********************/ + +bool SslBox_t::PutCiphertext (const char *buf, int bufsize) +{ + assert (buf && (bufsize > 0)); + + assert (pbioRead); + int n = BIO_write (pbioRead, buf, bufsize); + + return (n == bufsize) ? true : false; +} + + +/********************** +SslBox_t::GetPlaintext +**********************/ + +int SslBox_t::GetPlaintext (char *buf, int bufsize) +{ + if (!SSL_is_init_finished (pSSL)) { + int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL); + if (e < 0) { + int er = SSL_get_error (pSSL, e); + if (er != SSL_ERROR_WANT_READ) { + // Return -1 for a nonfatal error, -2 for an error that should force the connection down. + return (er == SSL_ERROR_SSL) ? (-2) : (-1); + } + else + return 0; + } + // If handshake finished, FALL THROUGH and return the available plaintext. + } + + if (!SSL_is_init_finished (pSSL)) { + // We can get here if a browser abandons a handshake. + // The user can see a warning dialog and abort the connection. + cerr << "<SSL_incomp>"; + return 0; + } + + //cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl; + + int n = SSL_read (pSSL, buf, bufsize); + if (n >= 0) { + return n; + } + else { + if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) { + return 0; + } + else { + return -1; + } + } + + return 0; +} + + + +/************************** +SslBox_t::CanGetCiphertext +**************************/ + +bool SslBox_t::CanGetCiphertext() +{ + assert (pbioWrite); + return BIO_pending (pbioWrite) ? true : false; +} + + + +/*********************** +SslBox_t::GetCiphertext +***********************/ + +int SslBox_t::GetCiphertext (char *buf, int bufsize) +{ + assert (pbioWrite); + assert (buf && (bufsize > 0)); + + return BIO_read (pbioWrite, buf, bufsize); +} + + + +/********************** +SslBox_t::PutPlaintext +**********************/ + +int SslBox_t::PutPlaintext (const char *buf, int bufsize) +{ + // The caller will interpret the return value as the number of bytes written. + // WARNING WARNING WARNING, are there any situations in which a 0 or -1 return + // from SSL_write means we should immediately retry? The socket-machine loop + // will probably wait for a time-out cycle (perhaps a second) before re-trying. + // THIS WOULD CAUSE A PERCEPTIBLE DELAY! + + /* We internally queue any outbound plaintext that can't be dispatched + * because we're in the middle of a handshake or something. + * When we get called, try to send any queued data first, and then + * send the caller's data (or queue it). We may get called with no outbound + * data, which means we try to send the outbound queue and that's all. + * + * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error. + * Note that if we return 0, the connection is still considered live + * and we are signalling that we have accepted the outbound data (if any). + */ + + OutboundQ.Push (buf, bufsize); + + if (!SSL_is_init_finished (pSSL)) + return 0; + + bool fatal = false; + bool did_work = false; + + while (OutboundQ.HasPages()) { + const char *page; + int length; + OutboundQ.Front (&page, &length); + assert (page && (length > 0)); + int n = SSL_write (pSSL, page, length); + if (n > 0) { + did_work = true; + OutboundQ.PopFront(); + } + else { + int er = SSL_get_error (pSSL, n); + if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE)) + fatal = true; + break; + } + } + + + if (did_work) + return 1; + else if (fatal) + return -1; + else + return 0; +} + + +#endif // WITH_SSL + diff --git a/test/scanners/cpp/pleac.expected.raydebug b/test/scanners/cpp/pleac.expected.raydebug new file mode 100644 index 0000000..62426ec --- /dev/null +++ b/test/scanners/cpp/pleac.expected.raydebug @@ -0,0 +1,2041 @@ +comment(// -*- c++ -*-) + +comment(// @@PLEAC@@_NAME) +comment(// @@SKIP@@ C++/STL/Boost) + + +comment(// @@PLEAC@@_WEB) +comment(// @@SKIP@@ http://www.research.att.com/~bs/C++.html) +comment(// @@SKIP@@ http://www.boost.org/) + + +comment(// @@PLEAC@@_1.0) +comment(// NOTE: Whilst it is perfectly valid to use Standard C Library, or GNU) +comment(// C Library, routines in C++ programs, the code examples here will, as) +comment(// far as possible, avoid doing so, instead using C++-specific functionality) +comment(// and idioms. In general:) +comment(// * I/O will be iostream-based [i.e. no 'scanf', 'printf', 'fgets' etc]) +comment(// * Container / iterator idioms based on the Standard Template Library [STL]) +comment(// will replace the built-in array / raw pointer idioms typically used in C) +comment(// * Boost Library functionality utilised wherever possible [the reason for) +comment(// this is that much of this functionality is likely to appear in the next) +comment(// C++ standard]) +comment(// * Error detection/handling will generally be exception-based [this is done) +comment(// to keep examples simple. Exception use is optional in C++, and is not as) +comment(// pervasive as it is in other languages like Java or C#]) +comment(// C-based solution(s\) to problem(s\) will be found in the corresponding section) +comment(// of PLEAC-C/Posix/GNU.) + +comment(// In C++, one can use the builtin 'char *' type or the 'string' type) +comment(// to represent strings. In this section, we will work with the C++) +comment(// library 'string' class.) + +comment(// Characteristics of 'string' types:) +comment(// - may be of any length) +comment(// - are defined within the std namespace) +comment(// - can be converted to a 'const char *' using std::string::c_str(\)) +comment(// - can be subscripted to access individual characters (e.g., str[3]) +comment(// returns the 4th character of the string) +comment(// - memory associated with strings is reclaimed automatically as strings) +comment(// go out of scope) +comment(// - strings cannot be used as true/false values (i.e., the following is not) +comment(// allowed: string s; if (s\) {}\)) + +comment(//-----------------------------) +comment(// Before using strings, you must include the <string> header file) +preprocessor(#include) include(<string>) + +comment(//-----------------------------) +comment(// To create a literal strings, you must use double quotes ("\). You cannot) +comment(// use single quotes. ) + +comment(//-----------------------------) +comment(// String variables must be declared -- if no value is given it's) +comment(// value is the empty string (""\). ) +ident(std)operator(::)pre_type(string) ident(s)operator(;) + +comment(//-----------------------------) +comment(// To insert special characters, quote the character with \\ +std::string s1 = "\\\\n"; // Two characters, \\ and n) +ident(std)operator(::)pre_type(string) ident(s2) operator(=) string<delimiter(")content(Jon )char(\\")content(Maddog)char(\\")content( Orwant)delimiter(")>operator(;) comment(// Literal double quotes) + +comment(//-----------------------------) +comment(// Strings can be declared in one of two ways) +ident(std)operator(::)pre_type(string) ident(s1) operator(=) string<delimiter(")content(assignment syntax)delimiter(")>operator(;) +ident(std)operator(::)pre_type(string) ident(s2)operator(()string<delimiter(")content(constructor syntax)delimiter(")>operator(\);) + +comment(//-----------------------------) +comment(// Multi-line strings.) +comment(// There is no equivalent to perl's "here" documents in c++) +ident(std)operator(::)pre_type(string) ident(s1) operator(=) string<delimiter(")content( +This is a multiline string started and finished with double +quotes that spans 4 lines (it contains 3 newline characters\). +)delimiter(")>operator(;) + +ident(std)operator(::)pre_type(string) ident(s2) operator(=) string<delimiter(")content(This is a multiline string started and finished with double +quotes that spans 2 lines (it contains 1 newline character\).)delimiter(")>operator(;) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.1) +ident(std)operator(::)pre_type(string) ident(s) operator(=) string<delimiter(")content(some string)delimiter(")>operator(;) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(value1) operator(=) ident(s)operator(.)ident(substr)operator(()ident(offset)operator(,) ident(length)operator(\);) +ident(std)operator(::)pre_type(string) ident(value2) operator(=) ident(s)operator(.)ident(substr)operator(()ident(offset)operator(\);) + +comment(// Unlike perl, the substr function returns a copy of the substring) +comment(// rather than a reference to the existing substring, thus using substr) +comment(// on the left hand side of an assignment statement will not modify ) +comment(// the original string. To get this functionality, you can use the) +comment(// std::string::replace function.) + +comment(// Using offsets and lengths) +ident(s)operator(.)ident(replace)operator(()ident(offset)operator(,) ident(length)operator(,) ident(newstring)operator(\);) +ident(s)operator(.)ident(replace)operator(()ident(offset)operator(,) ident(s)operator(.)ident(size)operator((\)-)ident(offset)operator(,) ident(newtail)operator(\);) + +comment(//-----------------------------) +comment(// The C++ string class doesn't have anything equivalent to perl's unpack.) +comment(// Instead, one can use C structures to import/export binary data) + +comment(//-----------------------------) +preprocessor(#include) include(<string>) +pre_type(string) ident(s) operator(=) string<delimiter(")content(This is what you have)delimiter(")>operator(;) + +ident(std)operator(::)pre_type(string) ident(first) operator(=) ident(s)operator(.)ident(substr)operator(()integer(0)operator(,) integer(1)operator(\);) comment(// "T") +ident(std)operator(::)pre_type(string) ident(second) operator(=) ident(s)operator(.)ident(substr)operator(()integer(5)operator(,) integer(2)operator(\);) comment(// "is") +ident(std)operator(::)pre_type(string) ident(rest) operator(=) ident(s)operator(.)ident(substr)operator(()integer(13)operator(\);) comment(// "you have") + +comment(// C++ strings do not support backwards indexing as perl does but ) +comment(// you can fake it out by subtracting the negative index from the) +comment(// string length) +ident(std)operator(::)pre_type(string) ident(last) operator(=) ident(s)operator(.)ident(substr)operator(()ident(s)operator(.)ident(size)operator((\)-)integer(1)operator(\);) comment(// "e") +ident(std)operator(::)pre_type(string) ident(end) operator(=) ident(s)operator(.)ident(substr)operator(()ident(s)operator(.)ident(size)operator((\)-)integer(4)operator(\);) comment(// "have") +ident(std)operator(::)pre_type(string) ident(piece) operator(=) ident(s)operator(.)ident(substr)operator(()ident(s)operator(.)ident(size)operator((\)-)integer(8)operator(,) integer(3)operator(\);) comment(// "you") + +comment(//-----------------------------) +preprocessor(#include) include(<string>) +preprocessor(#include) include(<iostream>) + +pre_type(string) ident(s)operator(()string<delimiter(")content(This is what you have)delimiter(")>operator(\);) +ident(std)operator(::)ident(cout) operator(<<) ident(s) operator(<<) ident(std)operator(::)ident(endl)operator(;) +comment(// This is what you have) + +ident(s)operator(.)ident(replace)operator(()integer(5)operator(,)integer(2)operator(,)string<delimiter(")content(wasn't)delimiter(")>operator(\);) comment(// change "is to "wasn't") +comment(// This wasn't what you have) + +ident(s)operator(.)ident(replace)operator(()ident(s)operator(.)ident(size)operator((\)-)integer(12)operator(,) integer(12)operator(,) string<delimiter(")content(ondrous)delimiter(")>operator(\);) comment(// "This wasn't wondrous") +comment(// This wasn't wonderous) + +ident(s)operator(.)ident(replace)operator(()integer(0)operator(,) integer(1)operator(,) string<delimiter(")delimiter(")>operator(\);) comment(// delete first character) +comment(// his wasn't wondrous) + +ident(s)operator(.)ident(replace)operator(()ident(s)operator(.)ident(size)operator((\)-)integer(10)operator(,) integer(10)operator(,) string<delimiter(")delimiter(")>operator(\);) comment(// delete last 10 characters) +comment(// his wasn') + +comment(//-----------------------------) +comment(// C++ does not have built-in support for the perl s///, m//, and tr/// ) +comment(// operators; however, similar results can be achieved in at least ) +comment(// two ways:) +comment(// - string operations such as string::find, string::rfind, etc.) +comment(// - the boost regular expression library (regex++\) supports perl) +comment(// regular expression syntax.) +comment(// TODO: Add examples of each.) + +comment(// MISSING: if (substr($string, -10\) =~ /pattern/\) {) +comment(// print "Pattern matches in last 10 characters\\n";) +comment(// }) + +comment(// MISSING: substr($string, 0, 5\) =~ s/is/at/g;) + +comment(//-----------------------------) +comment(// exchange the first and last letters in a string using substr and replace) +pre_type(string) ident(a) operator(=) string<delimiter(")content(make a hat)delimiter(")>operator(;) + +ident(std)operator(::)pre_type(string) ident(first) operator(=) ident(a)operator(.)ident(substr)operator(()integer(0)operator(,)integer(1)operator(\);) +ident(std)operator(::)pre_type(string) ident(last) operator(=) ident(a)operator(.)ident(substr)operator(()ident(a)operator(.)ident(size)operator((\)-)integer(1)operator(\);) + +ident(a)operator(.)ident(replace)operator(()integer(0)operator(,)integer(1)operator(,) ident(last)operator(\);) +ident(a)operator(.)ident(replace)operator(()ident(a)operator(.)ident(size)operator((\)-)integer(1)operator(,) integer(1)operator(,) ident(first)operator(\);) + +comment(// exchange the first and last letters in a string using indexing and swap) +preprocessor(#include) include(<algorithm>) +ident(std)operator(::)ident(swap)operator(()ident(a)operator([)integer(0)operator(],) ident(a)operator([)ident(a)operator(.)ident(size)operator((\)-)integer(1)operator(]\);) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.2) +comment(//-----------------------------) +comment(// C++ doesn't have functionality equivalent to the || and ||=. ) +comment(// If statements and trigraphs can be used instead.) +comment(//-----------------------------) +comment(// C++ doesn't have anything equivalent "defined". C++ variables) +comment(// cannot be used at all if they have not previously been defined.) + +comment(//-----------------------------) +comment(// Use b if b is not empty, else c) +ident(a) operator(=) ident(b)operator(.)ident(size)operator((\)) operator(?) ident(b) operator(:) ident(c)operator(;) + +comment(// Set x to y unless x is not empty) +reserved(if) operator(()ident(x)operator(.)ident(is_empty)operator((\)\)) ident(x) operator(=) ident(y)operator(;) + +comment(//-----------------------------) +ident(foo) operator(=) operator((!)ident(bar)operator(.)ident(is_empty)operator((\)\)) operator(?) ident(bar) operator(:) string<delimiter(")content(DEFAULT VALUE)delimiter(")>operator(;) + +comment(//-----------------------------) +comment(// NOTE: argv is declared as char *argv[] in C/C++. We assume) +comment(// the following code surrounds the following examples that deal) +comment(// with argv. Also, arguments to a program start at argv[1] -- argv[0]) +comment(// is the name of the executable that's running.) +preprocessor(#include) include(<string.h>) +pre_type(int) ident(main)operator(()pre_type(int) ident(argc)operator(,) pre_type(char) operator(*)ident(argv)operator([]\)) operator({) + pre_type(char) operator(**)ident(args) operator(=) ident(argv)operator(+)integer(1)operator(;) comment(// +1 skips argv[0], the name of the executable) + comment(// examples) +operator(}) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(dir) operator(=) operator((*)ident(args)operator(\)) operator(?) operator(*)ident(argv)operator(++) operator(:) string<delimiter(")content(/tmp)delimiter(")>operator(;) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(dir) operator(=) ident(argv)operator([)integer(1)operator(]) operator(?) ident(argv)operator([)integer(1)operator(]) operator(:) string<delimiter(")content(/tmp)delimiter(")>operator(;) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(dir) operator(=) operator(()ident(argc)operator(-)integer(1)operator(\)) operator(?) ident(argv)operator([)integer(1)operator(]) operator(:) string<delimiter(")content(/tmp)delimiter(")>operator(;) + +comment(//-----------------------------) +preprocessor(#include) include(<map>) +ident(std)operator(::)ident(map)operator(<)ident(std)operator(::)pre_type(string)operator(,)pre_type(int)operator(>) ident(count)operator(;) + +ident(count)operator([)ident(shell)operator(.)ident(size)operator((\)) operator(?) ident(shell) operator(:) string<delimiter(")content(/bin/sh)delimiter(")>operator(]++;) + +comment(//-----------------------------) +comment(// find the user name on Unix systems) +comment(// TODO: Simplify. This is too ugly and complex) +preprocessor(#include) include(<sys/types.h>) +preprocessor(#include) include(<unistd.h>) +preprocessor(#include) include(<pwd.h>) +preprocessor(#include) include("boost/lexical_cast.hpp") + +ident(std)operator(::)pre_type(string) ident(user)operator(;) +pre_type(char) operator(*)ident(msg) operator(=) integer(0)operator(;) +ident(passwd) operator(*)ident(pwd) operator(=) integer(0)operator(;) + +reserved(if) operator(() operator(()ident(msg) operator(=) ident(getenv)operator(()string<delimiter(")content(USER)delimiter(")>operator(\)\)) operator(||) + operator(()ident(msg) operator(=) ident(getenv)operator(()string<delimiter(")content(LOGNAME)delimiter(")>operator(\)\)) operator(||) + operator(()ident(msg) operator(=) ident(getlogin)operator((\)\)) operator(\)) + ident(user) operator(=) ident(msg)operator(;) +reserved(else) reserved(if) operator(()ident(pwd) operator(=) ident(getpwuid)operator(()ident(getuid)operator((\)\)\)) + ident(user) operator(=) ident(pwd)operator(->)ident(pw_name)operator(;) +reserved(else) + ident(user) operator(=) string<delimiter(")content(Unknown uid number )delimiter(")> operator(+) ident(boost)operator(::)ident(lexical_cast)operator(<)ident(std)operator(::)pre_type(string)operator(>()ident(getuid)operator((\)\);) + +comment(//-----------------------------) +reserved(if) operator(()ident(starting_point)operator(.)ident(is_empty)operator((\)\)) ident(starting_point) operator(=) string<delimiter(")content(Greenwich)delimiter(")>operator(;) + +comment(//-----------------------------) +comment(// Example using list. Other C++ STL containers work similarly.) +preprocessor(#include) include(<list>) +ident(list)operator(<)pre_type(int)operator(>) ident(a)operator(,) ident(b)operator(;) +reserved(if) operator(()ident(a)operator(.)ident(is_empty)operator((\)\)) ident(a) operator(=) ident(b)operator(;) comment(// copy only if a is empty) +ident(a) operator(=) operator((!)ident(b)operator(.)ident(is_empty)operator((\)\)) operator(?) ident(b) operator(:) ident(c)operator(;) comment(// asign b if b nonempty, else c) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.3) +comment(//-----------------------------) +preprocessor(#include) include(<algorithm>) +ident(std)operator(::)ident(swap)operator(()ident(a)operator(,) ident(b)operator(\);) + +comment(//-----------------------------) +ident(temp) operator(=) ident(a)operator(;) +ident(a) operator(=) ident(b)operator(;) +ident(b) operator(=) ident(temp)operator(;) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(a)operator(()string<delimiter(")content(alpha)delimiter(")>operator(\);) +ident(std)operator(::)pre_type(string) ident(b)operator(()string<delimiter(")content(omega)delimiter(")>operator(\);) +ident(std)operator(::)ident(swap)operator(()ident(a)operator(,)ident(b)operator(\);) + +comment(//-----------------------------) +comment(// The ability to exchange more than two variables at once is not ) +comment(// built into the C++ language or C++ standard libraries. However, you) +comment(// can use the boost tuple library to accomplish this.) +preprocessor(#include) include(<boost/tuple/tuple.hpp>) + +ident(boost)operator(::)ident(tie)operator(()ident(alpha)operator(,)ident(beta)operator(,)ident(production)operator(\)) + operator(=) ident(boost)operator(::)ident(make_tuple)operator(()string<delimiter(")content(January)delimiter(")>operator(,) string<delimiter(")content(March)delimiter(")>operator(,) string<delimiter(")content(August)delimiter(")>operator(\);) +comment(// move beta to alpha,) +comment(// move production to beta,) +comment(// move alpha to production) +ident(boost)operator(::)ident(tie)operator(()ident(alpha)operator(,) ident(beta)operator(,) ident(production)operator(\)) + operator(=) ident(boost)operator(::)ident(make_tuple)operator(()ident(beta)operator(,) ident(production)operator(,) ident(alpha)operator(\);) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.4) +comment(//-----------------------------) +comment(// There are several ways to convert between characters) +comment(// and integers. The examples assume the following declarations:) +pre_type(char) ident(ch)operator(;) +pre_type(int) ident(num)operator(;) + +comment(//-----------------------------) +comment(// Using implicit conversion) +ident(num) operator(=) ident(ch)operator(;) +ident(ch) operator(=) ident(num)operator(;) + +comment(//-----------------------------) +comment(// New-style C++ casts) +ident(ch) operator(=) reserved(static_cast)operator(<)pre_type(char)operator(>()ident(num)operator(\);) +ident(num) operator(=) reserved(static_cast)operator(<)pre_type(int)operator(>()ident(ch)operator(\);) + +comment(//-----------------------------) +comment(// Old-style C casts) +ident(ch) operator(=) operator(()pre_type(char)operator(\))ident(num)operator(;) +ident(num) operator(=) operator(()pre_type(int)operator(\))ident(ch)operator(;) + +comment(//-----------------------------) +comment(// Using the C++ stringstream class) +preprocessor(#include) include(<sstream>) comment(// On some older compilers, use <strstream>) +ident(std)operator(::)ident(stringstream) ident(a)operator(;) comment(// On some older compilers, use std::strstream) + +ident(a) operator(<<) ident(ch)operator(;) comment(// Append character to a string) +ident(a) operator(>>) ident(num)operator(;) comment(// Output character as a number) + +ident(a) operator(<<) ident(num)operator(;) comment(// Append number to a string) +ident(a) operator(>>) ident(ch)operator(;) comment(// Output number as a character) + +comment(//-----------------------------) +comment(// Using sprintf, printf) +pre_type(char) ident(str)operator([)integer(2)operator(];) comment(// Has to be length 2 to have room for NULL character) +ident(sprintf)operator(()ident(str)operator(,) string<delimiter(")content(%c)delimiter(")>operator(,) ident(num)operator(\);) +ident(printf)operator(()string<delimiter(")content(Number %d is character %c)char(\\n)delimiter(")>operator(,) ident(num)operator(,) ident(num)operator(\);) + +comment(//-----------------------------) +pre_type(int) ident(ascii_value) operator(=) char('e')operator(;) comment(// now 101) +pre_type(char) ident(character) operator(=) integer(101)operator(;) comment(// now 'e') + +comment(//-----------------------------) +ident(printf)operator(()string<delimiter(")content(Number %d is character %c)char(\\n)delimiter(")>operator(,) integer(101)operator(,) integer(101)operator(\);) + +comment(//-----------------------------) +comment(// Convert from HAL to IBM, character by character) +preprocessor(#include) include(<string>) +preprocessor(#include) include(<iostream>) + +ident(std)operator(::)pre_type(string) ident(ibm)operator(,) ident(hal) operator(=) string<delimiter(")content(HAL)delimiter(")>operator(;) +reserved(for) operator(()pre_type(unsigned) pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i)operator(<)ident(hal)operator(.)ident(size)operator((\);) operator(++)ident(i)operator(\)) + ident(ibm) operator(+=) ident(hal)operator([)ident(i)operator(]+)integer(1)operator(;) comment(// Add one to each ascii value) +ident(std)operator(::)ident(cout) operator(<<) ident(ibm) operator(<<) ident(std)operator(::)ident(endl)operator(;) comment(// prints "IBM") + +comment(//-----------------------------) +comment(// Convert hal from HAL to IBM) +preprocessor(#include) include(<string>) +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<functional>) comment(// For bind1st and plus<>) +preprocessor(#include) include(<algorithm>) comment(// For transform ) + +ident(std)operator(::)pre_type(string) ident(hal) operator(=) string<delimiter(")content(HAL)delimiter(")>operator(;) +ident(transform)operator(()ident(hal)operator(.)ident(begin)operator((\),) ident(hal)operator(.)ident(end)operator((\),) ident(hal)operator(.)ident(begin)operator((\),) + ident(bind1st)operator(()ident(plus)operator(<)pre_type(char)operator(>(\),)integer(1)operator(\)\);) +ident(std)operator(::)ident(cout) operator(<<) ident(hal) operator(<<) ident(std)operator(::)ident(endl)operator(;) comment(// prints "IBM") +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.5) +comment(//-----------------------------) +comment(// Since C++ strings can be accessed one character at a time,) +comment(// there's no need to do any processing on the string to convert) +comment(// it into an array of characters. ) +preprocessor(#include) include(<string>) +ident(std)operator(::)pre_type(string) ident(s)operator(;) + +comment(// Accessing characters using for loop and integer offsets) +reserved(for) operator(()pre_type(unsigned) pre_type(int) ident(i)operator(=)integer(0)operator(;) ident(i)operator(<)ident(s)operator(.)ident(size)operator((\);) operator(++)ident(i)operator(\)) operator({) + comment(// do something with s[i]) +operator(}) + +comment(// Accessing characters using iterators) +reserved(for) operator(()ident(std)operator(::)pre_type(string)operator(::)ident(iterator) ident(i)operator(=)ident(s)operator(.)ident(begin)operator((\);) ident(i)operator(!=)ident(s)operator(.)ident(end)operator((\);) operator(++)ident(i)operator(\)) operator({) + comment(// do something with *i) +operator(}) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(str) operator(=) string<delimiter(")content(an apple a day)delimiter(")>operator(;) +ident(std)operator(::)ident(map)operator(<)pre_type(char)operator(,)pre_type(int)operator(>) ident(seen)operator(;) + +reserved(for) operator(()ident(std)operator(::)pre_type(string)operator(::)ident(iterator) ident(i)operator(=)ident(str)operator(.)ident(begin)operator((\);) ident(i)operator(!=)ident(str)operator(.)ident(end)operator((\);) operator(++)ident(i)operator(\)) + ident(seen)operator([*)ident(i)operator(]++;) + +ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(unique chars are: )delimiter(")>operator(;) +reserved(for) operator(()ident(std)operator(::)ident(map)operator(<)pre_type(char)operator(,)pre_type(int)operator(>::)ident(iterator) ident(i)operator(=)ident(seen)operator(.)ident(begin)operator((\);) ident(i)operator(!=)ident(seen)operator(.)ident(end)operator((\);) operator(++)ident(i)operator(\)) + ident(std)operator(::)ident(cout) operator(<<) ident(i)operator(->)ident(first)operator(;) +ident(std)operator(::)ident(cout) operator(<<) ident(std)operator(::)ident(endl)operator(;) +comment(// unique chars are: adelnpy) + +comment(//-----------------------------) +pre_type(int) ident(sum) operator(=) integer(0)operator(;) +reserved(for) operator(()ident(std)operator(::)pre_type(string)operator(::)ident(iterator) ident(i)operator(=)ident(str)operator(.)ident(begin)operator((\);) ident(i)operator(!=)ident(str)operator(.)ident(end)operator((\);) operator(++)ident(i)operator(\)) + ident(sum) operator(+=) operator(*)ident(i)operator(;) +ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(sum is )delimiter(")> operator(<<) ident(sum) operator(<<) ident(std)operator(::)ident(endl)operator(;) +comment(// prints "sum is 1248" if str was "an appla a day") + + +comment(//-----------------------------) +comment(// MISSING: sysv-like checksum program) + +comment(//-----------------------------) +comment(// slowcat, emulate a slow line printer) +preprocessor(#include) include(<sys/time.h>) +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<fstream>) + +pre_type(int) ident(main)operator(()pre_type(int) ident(argc)operator(,) pre_type(char) operator(*)ident(argv)operator([]\)) operator({) + ident(timeval) ident(delay) operator(=) operator({) integer(0)operator(,) integer(50000) operator(};) comment(// Delay in { seconds, nanoseconds }) + pre_type(char) operator(**)ident(arg) operator(=) ident(argv)operator(+)integer(1)operator(;) + reserved(while) operator((*)ident(arg)operator(\)) operator({) comment(// For each file) + ident(std)operator(::)ident(ifstream) ident(file)operator((*)ident(arg)operator(++\);) + pre_type(char) ident(c)operator(;) + reserved(while) operator(()ident(file)operator(.)ident(get)operator(()ident(c)operator(\)\)) operator({) + ident(std)operator(::)ident(cout)operator(.)ident(put)operator(()ident(c)operator(\);) + ident(std)operator(::)ident(cout)operator(.)ident(flush)operator((\);) + ident(select)operator(()integer(0)operator(,) integer(0)operator(,) integer(0)operator(,) integer(0)operator(,) operator(&)ident(delay)operator(\);) + operator(}) + operator(}) +operator(}) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.6) +comment(//-----------------------------) +preprocessor(#include) include(<string>) +preprocessor(#include) include(<algorithm>) comment(// For reverse) +ident(std)operator(::)pre_type(string) ident(s)operator(;) + +ident(reverse)operator(()ident(s)operator(.)ident(begin)operator((\),) ident(s)operator(.)ident(end)operator((\)\);) + +comment(//-----------------------------) +preprocessor(#include) include(<vector>) comment(// For std::vector) +preprocessor(#include) include(<sstream>) comment(// On older compilers, use <strstream>) +preprocessor(#include) include("boost/regex.hpp") comment(// For boost::regex_split) + +ident(std)operator(::)pre_type(string) ident(str)operator(;) +ident(std)operator(::)ident(vector)operator(<)ident(std)operator(::)pre_type(string)operator(>) ident(words)operator(;) +ident(boost)operator(::)ident(regex_split)operator(()ident(std)operator(::)ident(back_inserter)operator(()ident(words)operator(\),) ident(str)operator(\);) +ident(reverse)operator(()ident(words)operator(.)ident(begin)operator((\),) ident(words)operator(.)ident(end)operator((\)\);) comment(// Reverse the order of the words) + +ident(std)operator(::)ident(stringstream) ident(revwords)operator(;) comment(// On older compilers, use strstream) +ident(copy)operator(()ident(words)operator(.)ident(begin)operator((\),) ident(words)operator(.)ident(end)operator((\),) ident(ostream_inserter)operator(<)pre_type(string)operator(>()ident(revwords)operator(,)string<delimiter(")content( )delimiter(")>operator(\);) +ident(std)operator(::)ident(cout) operator(<<) ident(revwards)operator(.)ident(str)operator((\)) operator(<<) ident(std)operator(::)ident(endl)operator(;) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(rts) operator(=) ident(str)operator(;) +ident(reverse)operator(()ident(rts)operator(.)ident(begin)operator((\),) ident(rts)operator(.)ident(end)operator((\)\);) comment(// Reverses letters in rts) + +comment(//-----------------------------) +ident(std)operator(::)ident(vector)operator(<)pre_type(string)operator(>) ident(words)operator(;) +ident(reverse)operator(()ident(words)operator(.)ident(begin)operator((\),) ident(words)operator(.)ident(end)operator((\)\);) comment(// Reverses words in container) + +comment(//-----------------------------) +comment(// Reverse word order) +ident(std)operator(::)pre_type(string) ident(s) operator(=) string<delimiter(")content(Yoda said, 'can you see this?')delimiter(")>operator(;) + +ident(std)operator(::)ident(vector)operator(<)ident(std)operator(::)pre_type(string)operator(>) ident(allwords)operator(;) +ident(boost)operator(::)ident(regex_split)operator(()ident(std)operator(::)ident(back_inserter)operator(()ident(allwords)operator(\),) ident(s)operator(\);) + +ident(reverse)operator(()ident(allwords)operator(.)ident(begin)operator((\),) ident(allwords)operator(.)ident(end)operator((\)\);) + +ident(std)operator(::)ident(stringstream) ident(revwords)operator(;) comment(// On older compilers, use strstream) +ident(copy)operator(()ident(allwords)operator(.)ident(begin)operator((\),) ident(allwords)operator(.)ident(end)operator((\),) ident(ostream_inserter)operator(<)pre_type(string)operator(>()ident(revwords)operator(,)string<delimiter(")content( )delimiter(")>operator(\)\);) +ident(std)operator(::)ident(cout) operator(<<) ident(revwards)operator(.)ident(str)operator((\)) operator(<<) ident(std)operator(::)ident(endl)operator(;) +comment(// this?' see you 'can said, Yoda) + +comment(//-----------------------------) +ident(std)operator(::)pre_type(string) ident(word) operator(=) string<delimiter(")content(reviver)delimiter(")>operator(;) +pre_type(bool) ident(is_palindrome) operator(=) ident(equal)operator(()ident(word)operator(.)ident(begin)operator((\),) ident(word)operator(.)ident(end)operator((\),) ident(word)operator(.)ident(rbegin)operator((\)\);) + +comment(//-----------------------------) +preprocessor(#include) include(<ifstream>) + +ident(std)operator(::)ident(ifstream) ident(dict)operator(()string<delimiter(")content(/usr/dict/words)delimiter(")>operator(\);) +ident(std)operator(::)pre_type(string) ident(word)operator(;) +reserved(while)operator(()ident(getline)operator(()ident(dict)operator(,)ident(word)operator(\)\)) operator({) + reserved(if) operator(()ident(equal)operator(()ident(word)operator(.)ident(begin)operator((\),) ident(word)operator(.)ident(end)operator((\),) ident(word)operator(.)ident(rbegin)operator((\)\)) operator(&&) + ident(word)operator(.)ident(size)operator((\)) operator(>) integer(5)operator(\)) + ident(std)operator(::)ident(cout) operator(<<) ident(word) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.7) +comment(//-----------------------------) +preprocessor(#include) include(<string>) + +ident(std)operator(::)pre_type(string)operator(::)ident(size_type) ident(pos)operator(;) +reserved(while) operator((()ident(pos) operator(=) ident(str)operator(.)ident(find)operator(()string<delimiter(")char(\\t)delimiter(")>operator(\)\)) operator(!=) ident(std)operator(::)pre_type(string)operator(::)ident(npos)operator(\)) + ident(str)operator(.)ident(replace)operator(()ident(pos)operator(,) integer(1)operator(,) pre_type(string)operator(()char(' ')operator(,)integer(8)operator(-)ident(pos)operator(%)integer(8)operator(\)\);) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.8) +comment(//-----------------------------) +comment(// Not applicable to C++) +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.9) +comment(//-----------------------------) +comment(// TODO: Fix to be more like cookbook) +comment(// TODO: Modify/add code to do this with locales) +preprocessor(#include) include(<string>) +preprocessor(#include) include(<algorithm>) + +ident(std)operator(::)pre_type(string) ident(phrase) operator(=) string<delimiter(")content(bo peep)delimiter(")>operator(;) +ident(transform)operator(()ident(phrase)operator(.)ident(begin)operator((\),) ident(phrase)operator(.)ident(end)operator((\),) ident(phrase)operator(.)ident(begin)operator((\),) ident(toupper)operator(\);) +comment(// "BO PEEP") +ident(transform)operator(()ident(phrase)operator(.)ident(begin)operator((\),) ident(phrase)operator(.)ident(end)operator((\),) ident(phrase)operator(.)ident(begin)operator((\),) ident(tolower)operator(\);) +comment(// "bo peep") +comment(//-----------------------------) + + +comment(// @@PLEAC@@_1.10) +comment(//-----------------------------) +comment(// C++ does not provide support for perl-like in-string interpolation,) +comment(// concatenation must be used instead.) + +preprocessor(#include) include(<string>) + +ident(std)operator(::)pre_type(string) ident(var1)operator(,) ident(var2)operator(;) +ident(std)operator(::)pre_type(string) ident(answer) operator(=) ident(var1) operator(+) ident(func)operator((\)) operator(+) ident(var2)operator(;) comment(// func returns string or char *) + +comment(//-----------------------------) +preprocessor(#include) include("boost/lexical_cast.hpp") + +pre_type(int) ident(n) operator(=) integer(4)operator(;) +ident(std)operator(::)pre_type(string) ident(phrase) operator(=) string<delimiter(")content(I have )delimiter(")> operator(+) ident(boost)operator(::)ident(lexical_cast)operator(<)pre_type(string)operator(>()ident(n)operator(+)integer(1)operator(\)) operator(+) string<delimiter(")content( guanacos.)delimiter(")>operator(;) + +comment(//-----------------------------) +ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(I have )delimiter(")> operator(+) ident(boost)operator(::)ident(lexical_cast)operator(<)pre_type(string)operator(>()ident(n)operator(+)integer(1)operator(\)) operator(+) string<delimiter(")content( guanacos.)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) + + +comment(// @@PLEAC@@_1.11) +comment(//-----------------------------) +comment(// C++ does not have "here documents".) +comment(// TODO: Lots more.) +preprocessor(#include) include(<string>) +preprocessor(#include) include("boost/regex.hpp") + +ident(std)operator(::)pre_type(string) ident(var) operator(=) string<delimiter(")content( + your text + goes here. +)delimiter(")>operator(;) + +ident(boost)operator(::)ident(regex) ident(ex)operator(()string<delimiter(")content(^)char(\\\\)content(s+)delimiter(")>operator(\);) +ident(var) operator(=) ident(boost)operator(::)ident(regex_merge)operator(()ident(var)operator(,) ident(ex)operator(,) string<delimiter(")delimiter(")>operator(\);) + +comment(// @@PLEAC@@_10.0) +comment(// NOTE: Whilst it is perfectly valid to use Standard C Library, or GNU C Library, routines in) +comment(// C++ programs, the code examples here will, as far as possible, avoid doing so, instead using) +comment(// C++-specific functionality and idioms. In general:) +comment(// * I/O will be iostream-based [i.e. no 'scanf', 'printf', 'fgets' etc]) +comment(// * Container / iterator idioms based on the Standard Template Library [STL]) +comment(// will replace the built-in array / raw pointer idioms typically used in C) +comment(// * Boost Library functionality utilised wherever possible [the reason for) +comment(// this is that much of this functionality is likely to appear in the next) +comment(// C++ standard]) +comment(// * Error detection/handling will generally be exception-based [this is done) +comment(// to keep examples simple. Exception use is optional in C++, and is not as) +comment(// pervasive as it is in other languages like Java or C#]) +comment(// C-based solution(s\) to problem(s\) will be found in the corresponding section of PLEAC-C/Posix/GNU.) + +preprocessor(#include) include(<iostream>) + +comment(// 'greeted' defined outside of any namespace, class or function, so is part of the) +comment(// global namespace, and will be visible throughout the entire executable. Should it) +comment(// be necessary to restrict the visibility of this global identifier to the current) +comment(// 'compilation unit' [i.e. current source file] then the following may be used:) +comment(//) +comment(// namespace { int greeted = 0; }) +comment(//) +comment(// The effect is similar to using the 'static' keyword, in this same context, in the C) +comment(// language.) + +pre_type(int) ident(greeted) operator(=) integer(0)operator(;) + +pre_type(int) ident(howManyGreetings)operator((\);) +directive(void) ident(hello)operator((\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + ident(hello)operator((\);) + + pre_type(int) ident(greetings) operator(=) ident(howManyGreetings)operator((\);) + + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(bye there!, there have been )delimiter(")> + operator(<<) ident(greetings) + operator(<<) string<delimiter(")content( greetings so far)delimiter(")> + operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ----) + +pre_type(int) ident(howManyGreetings)operator((\)) +operator({) + comment(// Access 'greeted' identifier in the global namespace using the scope resolution) + comment(// operator. Use of this operator is only necessary if a similarly-named identifier) + comment(// exists in a ) + reserved(return) operator(::)ident(greeted)operator(;) +operator(}) + +directive(void) ident(hello)operator((\)) +operator({) + comment(// Here 'greeted' is accessed without additional qualification. Since a 'greeted' identifier) + comment(// exists only in the global namespace, it is that identifier that is used) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(high there!, this function has been called )delimiter(")> + operator(<<) operator(++)ident(greeted) + operator(<<) string<delimiter(")content( times)delimiter(")> + operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// @@PLEAC@@_10.1) +comment(// Standard C++ requires that a function be prototyped, hence the name and type of parameters) +comment(// must be specified, and the argumemt list in any calls to that function must match the) +comment(// parameter list, as shown here ) + +preprocessor(#include) include(<cmath>) + +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) pre_type(double) ident(side2)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + pre_type(double) ident(diag) operator(=) ident(hypotenuse)operator(()float(3)float(.0)operator(,) float(4)float(.0)operator(\);) +operator(}) + +comment(// ----) + +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) pre_type(double) ident(side2)operator(\)) +operator({) + reserved(return) ident(std)operator(::)ident(sqrt)operator(()ident(std)operator(::)ident(pow)operator(()ident(side1)operator(,) float(2)float(.0)operator(\)) operator(+) ident(std)operator(::)ident(pow)operator(()ident(side2)operator(,) float(2)float(.0)operator(\)\);) +operator(}) + +comment(// ----------------------------) + +comment(// Variable length argument list functions, via the C Language derived 'va_...' macros,) +comment(// are also supported. However use of this facility is particularly discouraged in C++) +comment(// because:) +comment(// * It is an inherently type-unsafe facility; type safety is a core C++ concern) +comment(// * Other facilities, such as overloaded functions, and default arguments [neither of which) +comment(// are available in C] can sometimes obviate the need for variable length argument lists) +comment(// * OOP techniques can also lessen the need for variable length argument lists. The most) +comment(// obvious example here is the Iostream library where repeated calls of I/O operators replace) +comment(// the format string / variable arguments of 'printf') + +preprocessor(#include) include(<cmath>) +preprocessor(#include) include(<cstdarg>) + +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) operator(.)operator(.)operator(.)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + pre_type(double) ident(diag) operator(=) ident(hypotenuse)operator(()float(3)float(.0)operator(,) float(4)float(.0)operator(\);) +operator(}) + +comment(// ----) + +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) operator(.)operator(.)operator(.)operator(\)) +operator({) + comment(// More details available in the corresponding section of PLEAC-C/Posix/GNU) + ident(va_list) ident(ap)operator(;) + ident(va_start)operator(()ident(ap)operator(,) ident(side1)operator(\);) + pre_type(double) ident(side2) operator(=) ident(va_arg)operator(()ident(ap)operator(,) pre_type(double)operator(\);) + ident(va_end)operator(()ident(ap)operator(\);) + + reserved(return) ident(std)operator(::)ident(sqrt)operator(()ident(std)operator(::)ident(pow)operator(()ident(side1)operator(,) float(2)float(.0)operator(\)) operator(+) ident(std)operator(::)ident(pow)operator(()ident(side2)operator(,) float(2)float(.0)operator(\)\);) +operator(}) + +comment(// ----------------------------) + +comment(// An example using default arguments appears below) + +preprocessor(#include) include(<cmath>) + +comment(// Specify default argument values in declaration) +comment(// Note: This may be done in either of the declaration or the definition [not both], but it) +comment(// makes more sense to do so in the declaration since these are usually placed in header files) +comment(// which may be included in several source files. The default argument values would need to be) +comment(// known in all those locations) +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1) operator(=) float(3)float(.0)operator(,) pre_type(double) ident(side2) operator(=) float(4)float(.0)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// All arguments specified) + pre_type(double) ident(diag) operator(=) ident(hypotenuse)operator(()float(3)float(.0)operator(,) float(4)float(.0)operator(\);) + + comment(// Both calls utilise default argument value(s\)) + ident(diag) operator(=) ident(hypotenuse)operator(()float(3)float(.0)operator(\);) + + ident(diag) operator(=) ident(hypotenuse)operator((\);) +operator(}) + +comment(// ----) + +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) pre_type(double) ident(side2)operator(\)) +operator({) + reserved(return) ident(std)operator(::)ident(sqrt)operator(()ident(std)operator(::)ident(pow)operator(()ident(side1)operator(,) float(2)float(.0)operator(\)) operator(+) ident(std)operator(::)ident(pow)operator(()ident(side2)operator(,) float(2)float(.0)operator(\)\);) +operator(}) + +comment(// ----------------------------) + +comment(// A [very contrived, not very practical] example using function overloading appears below) + +preprocessor(#include) include(<cmath>) + +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) pre_type(double) ident(side2)operator(\);) +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(\);) +pre_type(double) ident(hypotenuse)operator((\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Call version (1\)) + pre_type(double) ident(diag) operator(=) ident(hypotenuse)operator(()float(3)float(.0)operator(,) float(4)float(.0)operator(\);) + + comment(// Call version (2\)) + ident(diag) operator(=) ident(hypotenuse)operator(()float(3)float(.0)operator(\);) + + comment(// Call version (3\)) + ident(diag) operator(=) ident(hypotenuse)operator((\);) +operator(}) + +comment(// ----) + +comment(// (1\)) +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(,) pre_type(double) ident(side2)operator(\)) +operator({) + reserved(return) ident(std)operator(::)ident(sqrt)operator(()ident(std)operator(::)ident(pow)operator(()ident(side1)operator(,) float(2)float(.0)operator(\)) operator(+) ident(std)operator(::)ident(pow)operator(()ident(side2)operator(,) float(2)float(.0)operator(\)\);) +operator(}) + +comment(// (2\)) +pre_type(double) ident(hypotenuse)operator(()pre_type(double) ident(side1)operator(\)) +operator({) + reserved(return) ident(std)operator(::)ident(sqrt)operator(()ident(std)operator(::)ident(pow)operator(()ident(side1)operator(,) float(2)float(.0)operator(\)) operator(+) ident(std)operator(::)ident(pow)operator(()float(4)float(.0)operator(,) float(2)float(.0)operator(\)\);) +operator(}) + +comment(// (3\)) +pre_type(double) ident(hypotenuse)operator((\)) +operator({) + reserved(return) ident(std)operator(::)ident(sqrt)operator(()ident(std)operator(::)ident(pow)operator(()float(3)float(.0)operator(,) float(2)float(.0)operator(\)) operator(+) ident(std)operator(::)ident(pow)operator(()float(4)float(.0)operator(,) float(2)float(.0)operator(\)\);) +operator(}) + +comment(// ----------------------------) + +preprocessor(#include) include(<cstddef>) +preprocessor(#include) include(<vector>) + +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(int_all)operator(()directive(const) pre_type(double) ident(arr)operator([],) ident(size_t) ident(arrsize)operator(\);) +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(int_all)operator(()directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(double)operator(>&) ident(arr)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Load vectors from built-in arrays, or use Boost 'assign' library) + directive(const) pre_type(double) ident(nums)operator([]) operator(=) operator({)float(1)float(.4)operator(,) float(3)float(.5)operator(,) float(6)float(.7)operator(};) + directive(const) ident(size_t) ident(arrsize) operator(=) reserved(sizeof)operator(()ident(nums)operator(\)) operator(/) reserved(sizeof)operator(()ident(nums)operator([)integer(0)operator(]\);) + + comment(// Conversion effected at vector creation time) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(ints) operator(=) ident(int_all)operator(()ident(nums)operator(,) ident(arrsize)operator(\);) + + comment(// Vector -> vector copy / conversion ) + ident(ints) operator(=) ident(int_all)operator(()ident(std)operator(::)ident(vector)operator(<)pre_type(double)operator(>()ident(nums)operator(,) ident(nums) operator(+) ident(arrsize)operator(\)\);) +operator(}) + +comment(// ----) + +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(int_all)operator(()directive(const) pre_type(double) ident(arr)operator([],) ident(size_t) ident(arrsize)operator(\)) +operator({) + reserved(return) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>()ident(arr)operator(,) ident(arr) operator(+) ident(arrsize)operator(\);) +operator(}) + +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(int_all)operator(()directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(double)operator(>&) ident(arr)operator(\)) +operator({) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(r)operator(;) + ident(r)operator(.)ident(assign)operator(()ident(arr)operator(.)ident(begin)operator((\),) ident(arr)operator(.)ident(end)operator((\)\);) comment(// Type safe element copying ) + reserved(return) ident(r)operator(;) +operator(}) + +comment(// ----------------------------) + +preprocessor(#include) include(<algorithm>) +preprocessor(#include) include(<vector>) + +preprocessor(#include) include(<cmath>) +preprocessor(#include) include(<cstddef>) + +directive(void) ident(trunc_em)operator(()ident(std)operator(::)ident(vector)operator(<)pre_type(double)operator(>&) ident(arr)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Load vectors from built-in arrays, or use Boost 'assign' library) + directive(const) pre_type(double) ident(nums)operator([]) operator(=) operator({)float(1)float(.4)operator(,) float(3)float(.5)operator(,) float(6)float(.7)operator(};) + directive(const) ident(size_t) ident(arrsize) operator(=) reserved(sizeof)operator(()ident(nums)operator(\)) operator(/) reserved(sizeof)operator(()ident(nums)operator([)integer(0)operator(]\);) + + ident(std)operator(::)ident(vector)operator(<)pre_type(double)operator(>) ident(numsv)operator(()ident(nums)operator(,) ident(nums) operator(+) ident(arrsize)operator(\);) + + ident(trunc_em)operator(()ident(numsv)operator(\);) +operator(}) + +comment(// ----) + +directive(void) ident(trunc_em)operator(()ident(std)operator(::)ident(vector)operator(<)pre_type(double)operator(>&) ident(arr)operator(\)) +operator({) + comment(// Replace each element with the value returned by applying 'floor' to that element) + ident(std)operator(::)ident(transform)operator(()ident(arr)operator(.)ident(begin)operator((\),) ident(arr)operator(.)ident(end)operator((\),) ident(arr)operator(.)ident(begin)operator((\),) ident(floor)operator(\);) +operator(}) + +comment(// @@PLEAC@@_10.2) +comment(// Variables declared within a function body are local to that function, and those declared) +comment(// outside a function body [and not as part of a class / struct definition, or enclosed within) +comment(// a namespace] are global, that is, are visible throughout the executable unless their) +comment(// visibility has been restricted to the source file in which they are defined via enclosing) +comment(// them within an anonymous namespace [which has the same effect as using the 'static' keyword,) +comment(// in this same context, in the C language]) + +preprocessor(#include) include(<vector>) + +directive(void) ident(somefunc)operator((\)) +operator({) + comment(// All these variables are local to this function) + pre_type(int) ident(variable)operator(,) ident(another)operator(;) + + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(vec)operator(()integer(5)operator(\);) + + operator(;) comment(// ...) +operator(}) + +comment(// ----------------------------) + +comment(// A couple of generic, type-safe type conversion helpers. The Boost Library sports a conversion) +comment(// library at: http://www.boost.org/libs/conversion/index.html) + +preprocessor(#include) include(<sstream>) +preprocessor(#include) include(<string>) + +reserved(class) class(bad_conversion) operator({};) + +reserved(template)operator(<)reserved(typename) ident(T)operator(>) ident(T) ident(fromString)operator(()directive(const) ident(std)operator(::)pre_type(string)operator(&) ident(s)operator(\)) +operator({) + ident(std)operator(::)ident(istringstream) ident(iss)operator(()ident(s)operator(\);) + ident(T) ident(t)operator(;) ident(iss) operator(>>) ident(t)operator(;) + reserved(if) operator((!)ident(iss)operator(\)) reserved(throw) ident(bad_conversion)operator((\);) + reserved(return) ident(t)operator(;) +operator(}) + +reserved(template)operator(<)reserved(typename) ident(T)operator(>) ident(std)operator(::)pre_type(string) ident(toString)operator(()directive(const) ident(T)operator(&) ident(t)operator(\)) +operator({) + ident(std)operator(::)ident(ostringstream) ident(oss)operator(;) + ident(oss) operator(<<) ident(t) operator(<<) ident(std)operator(::)ident(ends)operator(;) + reserved(if) operator((!)ident(oss)operator(\)) reserved(throw) ident(bad_conversion)operator((\);) + reserved(return) ident(std)operator(::)pre_type(string)operator(()ident(oss)operator(.)ident(str)operator((\)\);) +operator(}) + +comment(// ------------) + +preprocessor(#include) include(<string>) + +comment(// File scope variables) +reserved(namespace) +operator({) + ident(std)operator(::)pre_type(string) ident(name)operator(;) + pre_type(int) ident(age)operator(,) ident(c)operator(,) ident(condition)operator(;) +operator(}) + +directive(void) ident(run_check)operator((\);) +directive(void) ident(check_x)operator(()pre_type(int) ident(x)operator(\);) + +comment(// ----) + +comment(// An alternative, C++-specific approach, to command-line handling and type conversion) +comment(// may be seen at: http://www.boost.org/libs/conversion/lexical_cast.htm) + +pre_type(int) ident(main)operator(()pre_type(int) ident(argc)operator(,) pre_type(char)operator(*) ident(argv)operator([]\)) +operator({) + ident(name)operator(.)ident(assign)operator(()ident(argv)operator([)integer(1)operator(]\);) + + reserved(try) + operator({) + ident(age) operator(=) ident(fromString)operator(<)pre_type(int)operator(>()ident(argv)operator([)integer(2)operator(]\);) + operator(}) + + reserved(catch) operator(()directive(const) ident(bad_conversion)operator(&) ident(e)operator(\)) + operator({) + operator(;) comment(// ... handle conversion error ...) + operator(}) + + ident(check_x)operator(()ident(age)operator(\);) +operator(}) + +comment(// ------------) + +directive(void) ident(run_check)operator((\)) +operator({) + comment(// Full access to file scope variables) + ident(condition) operator(=) integer(1)operator(;) + comment(// ...) +operator(}) + +directive(void) ident(check_x)operator(()pre_type(int) ident(x)operator(\)) +operator({) + comment(// Full access to file scope variables) + ident(std)operator(::)pre_type(string) ident(y)operator(()string<delimiter(")content(whatever)delimiter(")>operator(\);) + + ident(run_check)operator((\);) + + comment(// 'condition' updated by 'run_check') + reserved(if) operator(()ident(condition)operator(\)) + operator({) + operator(;) comment(// ...) + operator(}) +operator(}) + +comment(// @@PLEAC@@_10.3) +comment(// Standard C++, owing to its C heritage, allows the creation of 'persistent private variables',) +comment(// via use of the 'static' keyword. For more details about this, and illustrative code examples,) +comment(// refer to this same section in PLEAC-C/Posix/GNU. Standard C++-specific methods of perfoming) +comment(// this task involve use of the 'namespace' facility, or creating a class containing 'static') +comment(// members and using access specifiers to restrict access) + +comment(// This example replaces the 'static' keyword with use of an anonymous namespace to force) +comment(// 'variable' to have file scope, and be visible only within the 'mysubs.cpp file. It is) +comment(// therefore both persistant [because it is a global variable] and private [because it is) +comment(// visible only to functions defined within the same source file]) + +comment(// File: 'mysubs.h') +directive(void) ident(mysub)operator(()directive(void)operator(\);) +directive(void) ident(reset)operator(()directive(void)operator(\);) + +comment(// ----) + +comment(// File: 'mysubs.cpp') +reserved(namespace) +operator({) + pre_type(int) ident(variable) operator(=) integer(1)operator(;) +operator(}) + +directive(void) ident(mysub)operator(()directive(void)operator(\)) +operator({) + operator(;) comment(// ... do something with 'variable' ...) +operator(}) + +directive(void) ident(reset)operator(()directive(void)operator(\)) operator({) ident(variable) operator(=) integer(1)operator(;) operator(}) + +comment(// ----) + +comment(// File: 'test.cpp') +preprocessor(#include) include("mysubs.h") + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// 'variable' is not accessable here) + + comment(// Call 'mysub', which can access 'variable') + ident(mysub)operator((\);) + + comment(// Call 'reset' which sets 'variable' to 1 ) + ident(reset)operator((\);) +operator(}) + +comment(// ------------) + +comment(// This example is similar to the previous one in using an anonymous namespace to restrict) +comment(// variable visibility. It goes further, hoewever, grouping logically related items within) +comment(// a named namespace, thus ensuring access to those items is controlled [i.e. requires) +comment(// qualification, or a 'using' declaration or directive]) + +comment(// File: 'counter.h') +reserved(namespace) ident(cnt) +operator({) + pre_type(int) ident(increment)operator((\);) + pre_type(int) ident(decrement)operator((\);) +operator(}) + +comment(// ----) + +comment(// File: 'counter.cpp') +reserved(namespace) ident(cnt) +operator({) + comment(// Ensures 'counter' is visible only within the current source file) + reserved(namespace) operator({) pre_type(int) ident(counter) operator(=) integer(0)operator(;) operator(}) + + directive(void) ident(reset)operator(()pre_type(int) ident(v) operator(=) integer(0)operator(\)) operator({) ident(counter) operator(=) ident(v)operator(;) operator(}) + + pre_type(int) ident(increment)operator((\)) operator({) reserved(return) operator(++)ident(counter)operator(;) operator(}) + pre_type(int) ident(decrement)operator((\)) operator({) reserved(return) operator(--)ident(counter)operator(;) operator(}) +operator(}) + +comment(// ----) + +comment(// File: 'test.cpp') +preprocessor(#include) include(<iostream>) +preprocessor(#include) include("counter.h") + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Following line is illegal because 'cnt::counter' is private to the 'counter.cpp' file) + comment(// int c = cnt::counter;) + + pre_type(int) ident(a) operator(=) ident(cnt)operator(::)ident(increment)operator((\);) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) ident(std)operator(::)ident(endl)operator(;) + + ident(a) operator(=) ident(cnt)operator(::)ident(decrement)operator((\);) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ------------) + +comment(// This example sees a class containing 'static' members and using access specifiers to) +comment(// restrict access to those members. Since all the members are static, this class is not) +comment(// meant to be instantiated [i.e. objects created from it - it can be done, but they would) +comment(// all be the exact same object :\)], but merely uses the 'class' facility to encapsulate) +comment(// [i.e. group together] and allow selective access [i.e. hide some parts, allow access to) +comment(// others]. For Design Pattern afficiandos, this is a crude example of the Singleton Pattern) + +comment(// File: 'counter.h') +reserved(class) class(Counter) +operator({) +directive(public)operator(:) + directive(static) pre_type(int) ident(increment)operator((\);) + directive(static) pre_type(int) ident(decrement)operator((\);) +directive(private)operator(:) + directive(static) pre_type(int) ident(counter)operator(;) +operator(};) + +comment(// ----) + +comment(// File: 'counter.cpp') +preprocessor(#include) include("counter.h") + +pre_type(int) ident(Counter)operator(::)ident(increment)operator((\)) operator({) reserved(return) operator(++)ident(counter)operator(;) operator(}) +pre_type(int) ident(Counter)operator(::)ident(decrement)operator((\)) operator({) reserved(return) operator(--)ident(counter)operator(;) operator(}) + +pre_type(int) ident(Counter)operator(::)ident(counter) operator(=) integer(0)operator(;) + +comment(// ----) + +comment(// File: 'test.cpp') +preprocessor(#include) include(<iostream>) +preprocessor(#include) include("counter.h") + +pre_type(int) ident(main)operator((\)) +operator({) + pre_type(int) ident(a) operator(=) ident(Counter)operator(::)ident(increment)operator((\);) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) ident(std)operator(::)ident(endl)operator(;) + + ident(a) operator(=) ident(Counter)operator(::)ident(decrement)operator((\);) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// @@PLEAC@@_10.4) +comment(// Standard C++ offers no facility for performing adhoc, runtime stack inspection; therefore,) +comment(// information such as the currently-executing function name, cannot be obtained. Now, this) +comment(// isn't to say that such facilities don't exist [since, after all, a symbolic debugger works) +comment(// by doing just this - stack inspection, among other things], but that such features are, for) +comment(// native code compiled languages like C++, 'extra-language' and development tool-specific) + +comment(// @@PLEAC@@_10.5) +comment(// Standard C++ supports both) +comment(// * 'pass-by-value': a copy of an argument is passed when calling a function; in this way) +comment(// the original is safe from modification, but a copying overhead is incurred which may) +comment(// adversely affect performance) +comment(// * 'pass-by-reference': the address of an argument is passed when calling a function;) +comment(// allows the original to be modified, and incurrs no performance penalty from copying) +comment(//) +comment(// The 'pass-by-value' mechanism works in the same way as in the Standard C language [see) +comment(// corresponding section in PLEAC-C/Posix/GNU]. The 'pass-by-reference' mechanism provides) +comment(// the same functionality as passing a pointer-to-a-pointer-to-an-argument, but without the) +comment(// complications arising from having to correctly dereference. Using a reference to a non-const) +comment(// item allows:) +comment(// * The item's state to be modified i.e. if an object was passed, it can be mutated [effect) +comment(// can be mimiced by passing a pointer to the item]) +comment(// * The item, itself, can be replaced with a new item i.e. the memory location to which the) +comment(// reference refers is updated [effect can be mimiced by passing a pointer-to-a-pointer to) +comment(// the item]) + +preprocessor(#include) include(<cstddef>) +preprocessor(#include) include(<vector>) + +comment(// 'pass-by-value': a copy of each vector is passed as an argument) +comment(// void array_diff(const std::vector<int> arr1, const std::vector<int> arr2\);) + +comment(// 'pass-by-reference': the address of each vector is passed as an argument. Some variants:) +comment(// * Disallow both vector replacement and alteration of its contents) +comment(// void array_diff(const std::vector<const int>& arr1, const std::vector<const int>& arr2\);) +comment(// * Disallow vector replacement only) +comment(// void array_diff(const std::vector<int>& arr1, const std::vector<int>& arr2\);) +comment(// * Disallow alteration of vector contents only) +comment(// void array_diff(std::vector<const int>& arr1, std::vector<const int>& arr2\);) +comment(// * Allow replacement / alteration) +comment(// void array_diff(std::vector<int>& arr1, std::vector<int>& arr2\);) + +directive(void) ident(array_diff)operator(()directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr1)operator(,) directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr2)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Load vectors from built-in arrays, or use Boost 'assign' library) + directive(const) pre_type(int) ident(arr1)operator([]) operator(=) operator({)integer(1)operator(,) integer(2)operator(,) integer(3)operator(},) ident(arr2)operator([]) operator(=) operator({)integer(4)operator(,) integer(5)operator(,) integer(6)operator(};) + directive(const) ident(size_t) ident(arrsize) operator(=) integer(3)operator(;) + + comment(// Function call is the same whether 'array_diff' is declared to be 'pass-by-value') + comment(// or 'pass-by-reference') + ident(array_diff)operator(()ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>()ident(arr1)operator(,) ident(arr1) operator(+) ident(arrsize)operator(\),) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>()ident(arr2)operator(,) ident(arr2) operator(+) ident(arrsize)operator(\)\);) +operator(}) + +comment(// ----) + +comment(// void array_diff(const std::vector<int> arr1, const std::vector<int> arr2\)) +comment(// {) +comment(// ; // 'arr1' and 'arr2' are copies of the originals) +comment(// }) + +directive(void) ident(array_diff)operator(()directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr1)operator(,) directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr2)operator(\)) +operator({) + operator(;) comment(// 'arr1' and 'arr2' are references to the originals) +operator(}) + +comment(// ----------------------------) + +preprocessor(#include) include(<cstddef>) + +preprocessor(#include) include(<algorithm>) +preprocessor(#include) include(<functional>) +preprocessor(#include) include(<vector>) + +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(add_vecpair)operator(()directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr1)operator(,) directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr2)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Load vectors from built-in arrays, or use Boost 'assign' library) + directive(const) pre_type(int) ident(aa)operator([]) operator(=) operator({)integer(1)operator(,) integer(2)operator(},) ident(ba)operator([]) operator(=) operator({)integer(5)operator(,) integer(8)operator(};) + ident(size_t) ident(arrsize) operator(=) integer(2)operator(;) + + directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(a)operator(()ident(aa)operator(,) ident(aa) operator(+) ident(arrsize)operator(\),) ident(b)operator(()ident(ba)operator(,) ident(ba) operator(+) ident(arrsize)operator(\);) + + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(c) operator(=) ident(add_vecpair)operator(()ident(a)operator(,) ident(b)operator(\);) +operator(}) + +comment(// ----) + +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(add_vecpair)operator(()directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr1)operator(,) directive(const) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>&) ident(arr2)operator(\)) +operator({) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(retvec)operator(;) ident(retvec)operator(.)ident(reserve)operator(()ident(arr1)operator(.)ident(size)operator((\)\);) + ident(std)operator(::)ident(transform)operator(()ident(arr1)operator(.)ident(begin)operator((\),) ident(arr1)operator(.)ident(end)operator((\),) ident(arr2)operator(.)ident(begin)operator((\),) ident(back_inserter)operator(()ident(retvec)operator(\),) ident(std)operator(::)ident(plus)operator(<)pre_type(int)operator(>(\)\);) + reserved(return) ident(retvec)operator(;) +operator(}) + +comment(// @@PLEAC@@_10.6) +comment(// Please refer to the corresponding section in PLEAC-C/Posix/GNU since the points raised there) +comment(// apply to C++ also. Examples here don't so much illustrate C++'s handling of 'return context') +comment(// as much as how disparate types might be handled in a reasonably uniform manner) + +comment(// Here, 'mysub' is implemented as a function template, and its return type varies with the) +comment(// argument type. In most cases the compiler is able to infer the return type from the ) +comment(// argument, however, it is possible to pass the type as a template parameter. Note this) +comment(// code operates at compile-time, as does any template-only code) + +preprocessor(#include) include(<cstddef>) + +preprocessor(#include) include(<string>) +preprocessor(#include) include(<vector>) + +reserved(template) operator(<)reserved(typename) ident(T)operator(>) ident(T) ident(mysub)operator(()directive(const) ident(T)operator(&) ident(t)operator(\)) operator({) reserved(return) ident(t)operator(;) operator(}) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// 1. Type information inferred by compiler) + pre_type(int) ident(i) operator(=) ident(mysub)operator(()integer(5)operator(\);) + + pre_type(double) ident(d) operator(=) ident(mysub)operator(()float(7)float(.6)operator(\);) + + directive(const) pre_type(int) ident(arr)operator([]) operator(=) operator({)integer(1)operator(,) integer(2)operator(,) integer(3)operator(};) + directive(const) ident(size_t) ident(arrsize) operator(=) reserved(sizeof)operator(()ident(arr)operator(\)) operator(/) reserved(sizeof)operator(()ident(arr)operator([)integer(0)operator(]\);) + + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(v) operator(=) ident(mysub)operator(()ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>()ident(arr)operator(,) ident(arr) operator(+) ident(arrsize)operator(\)\);) + + comment(// 2. Type information provided by user) + comment(// Pass a 'const char*' argument and specify type information in the call) + ident(std)operator(::)pre_type(string) ident(s) operator(=) ident(mysub)operator(<)ident(std)operator(::)pre_type(string)operator(>()string<delimiter(")content(xyz)delimiter(")>operator(\);) + + comment(// Could avoid specifying type information by passing a 'std::string' argument ) + comment(// std::string s = mysub(std::string("xyz"\)\);) +operator(}) + +comment(// ----------------------------) + +comment(// This is a variant on the previous example that uses the Boost Library's 'any' type as a) +comment(// generic 'stub' type) + +preprocessor(#include) include(<string>) +preprocessor(#include) include(<vector>) + +preprocessor(#include) include(<boost/any.hpp>) + +reserved(template) operator(<)reserved(typename) ident(T)operator(>) ident(boost)operator(::)ident(any) ident(mysub)operator(()directive(const) ident(T)operator(&) ident(t)operator(\)) operator({) reserved(return) ident(boost)operator(::)ident(any)operator(()ident(t)operator(\);) operator(}) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + ident(std)operator(::)ident(vector)operator(<)ident(boost)operator(::)ident(any)operator(>) ident(any)operator(;) + + comment(// Add various types [encapsulated in 'any' objects] to the container) + ident(any)operator(.)ident(push_back)operator(()ident(mysub)operator(()integer(5)operator(\)\);) + ident(any)operator(.)ident(push_back)operator(()ident(mysub)operator(()float(7)float(.6)operator(\)\);) + ident(any)operator(.)ident(push_back)operator(()ident(mysub)operator(()ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>()integer(5)operator(,) integer(5)operator(\)\)\);) + ident(any)operator(.)ident(push_back)operator(()ident(mysub)operator(()ident(std)operator(::)pre_type(string)operator(()string<delimiter(")content(xyz)delimiter(")>operator(\)\)\);) + + comment(// Extract the various types from the container by appropriately casting the relevant) + comment(// 'any' object) + pre_type(int) ident(i) operator(=) ident(boost)operator(::)ident(any_cast)operator(<)pre_type(int)operator(>()ident(any)operator([)integer(0)operator(]\);) + pre_type(double) ident(d) operator(=) ident(boost)operator(::)ident(any_cast)operator(<)pre_type(double)operator(>()ident(any)operator([)integer(1)operator(]\);) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(v) operator(=) ident(boost)operator(::)ident(any_cast)operator(<) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) operator(>()ident(any)operator([)integer(2)operator(]\);) + ident(std)operator(::)pre_type(string) ident(s) operator(=) ident(boost)operator(::)ident(any_cast)operator(<)ident(std)operator(::)pre_type(string)operator(>()ident(any)operator([)integer(3)operator(]\);) +operator(}) + +comment(// @@PLEAC@@_10.7) +comment(// Just like the C language, C++ offers no support for named / keyword parameters. It is of) +comment(// course possible to mimic such functionality the same way it is done in C [see corresponding) +comment(// section in PLEAC-C/Posix/GNU], the most obvious means being by passing a set of key/value) +comment(// pairs in a std::map. This will not be shown here. Instead, two quite C++-specific examples) +comment(// will be provided, based on:) +comment(//) +comment(// * Named Parameter Idiom [see: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18]) +comment(// * Boost 'parameter' Library [see: http://www.boost.org/libs/parameter/doc/html/index.html]) + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<map>) + +reserved(class) class(TimeEntry) +operator({) +directive(public)operator(:) + directive(explicit) ident(TimeEntry)operator(()pre_type(int) ident(value) operator(=) integer(0)operator(,) pre_type(char) ident(dim) operator(=) char('s')operator(\);) + + pre_type(bool) directive(operator)operator(<()directive(const) ident(TimeEntry)operator(&) ident(right)operator(\)) directive(const)operator(;) + + directive(friend) ident(std)operator(::)ident(ostream)operator(&) directive(operator)operator(<<()ident(std)operator(::)ident(ostream)operator(&) ident(out)operator(,) directive(const) ident(TimeEntry)operator(&) ident(t)operator(\);) + +directive(private)operator(:) + pre_type(int) ident(value_)operator(;) + pre_type(char) ident(dim_)operator(;) +operator(};) + +reserved(typedef) ident(std)operator(::)ident(pair)operator(<)directive(const) pre_type(int)operator(,) ident(TimeEntry)operator(>) ident(TENTRY)operator(;) +reserved(typedef) ident(std)operator(::)ident(map)operator(<)directive(const) pre_type(int)operator(,) ident(TimeEntry)operator(>) ident(TIMETBL)operator(;) + +reserved(class) class(RaceTime) +operator({) +directive(public)operator(:) + directive(const) directive(static) pre_type(int) ident(START_TIME)operator(,) ident(FINISH_TIME)operator(,) ident(INCR_TIME)operator(;) + +directive(public)operator(:) + directive(explicit) ident(RaceTime)operator((\);) + + ident(RaceTime)operator(&) ident(start_time)operator(()directive(const) ident(TimeEntry)operator(&) ident(time)operator(\);) + ident(RaceTime)operator(&) ident(finish_time)operator(()directive(const) ident(TimeEntry)operator(&) ident(time)operator(\);) + ident(RaceTime)operator(&) ident(incr_time)operator(()directive(const) ident(TimeEntry)operator(&) ident(time)operator(\);) + + directive(friend) ident(std)operator(::)ident(ostream)operator(&) directive(operator)operator(<<()ident(std)operator(::)ident(ostream)operator(&) ident(out)operator(,) directive(const) ident(RaceTime)operator(&) ident(r)operator(\);) + +directive(private)operator(:) + ident(TIMETBL) ident(timetbl_)operator(;) +operator(};) + +directive(const) pre_type(int) ident(RaceTime)operator(::)ident(START_TIME) operator(=) integer(0)operator(,) ident(RaceTime)operator(::)ident(FINISH_TIME) operator(=) integer(1)operator(,) ident(RaceTime)operator(::)ident(INCR_TIME) operator(=) integer(2)operator(;) + +directive(void) ident(the_func)operator(()directive(const) ident(RaceTime)operator(&) ident(r)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + ident(the_func)operator(()ident(RaceTime)operator((\))operator(.)ident(start_time)operator(()ident(TimeEntry)operator(()integer(20)operator(,) char('s')operator(\)\))operator(.)ident(finish_time)operator(()ident(TimeEntry)operator(()integer(5)operator(,) char('m')operator(\)\))operator(.)ident(incr_time)operator(()ident(TimeEntry)operator(()integer(5)operator(,) char('s')operator(\)\)\);) + + ident(the_func)operator(()ident(RaceTime)operator((\))operator(.)ident(start_time)operator(()ident(TimeEntry)operator(()integer(5)operator(,) char('m')operator(\)\))operator(.)ident(finish_time)operator(()ident(TimeEntry)operator(()integer(30)operator(,) char('m')operator(\)\)\);) + + ident(the_func)operator(()ident(RaceTime)operator((\))operator(.)ident(start_time)operator(()ident(TimeEntry)operator(()integer(30)operator(,) char('m')operator(\)\)\);) +operator(}) + +comment(// ----) + +ident(std)operator(::)ident(ostream)operator(&) directive(operator)operator(<<()ident(std)operator(::)ident(ostream)operator(&) ident(out)operator(,) directive(const) ident(TimeEntry)operator(&) ident(t)operator(\)) +operator({) + ident(out) operator(<<) ident(t)operator(.)ident(value_) operator(<<) ident(t)operator(.)ident(dim_)operator(;) reserved(return) ident(out)operator(;) +operator(}) + +ident(std)operator(::)ident(ostream)operator(&) directive(operator)operator(<<()ident(std)operator(::)ident(ostream)operator(&) ident(out)operator(,) directive(const) ident(RaceTime)operator(&) ident(r)operator(\)) +operator({) + ident(RaceTime)operator(&) ident(r_) operator(=) reserved(const_cast)operator(<)ident(RaceTime)operator(&>()ident(r)operator(\);) + + ident(out) operator(<<) string<delimiter(")content(start_time: )delimiter(")> operator(<<) ident(r_)operator(.)ident(timetbl_)operator([)ident(RaceTime)operator(::)ident(START_TIME)operator(]) + operator(<<) string<delimiter(")char(\\n)content(finish_time: )delimiter(")> operator(<<) ident(r_)operator(.)ident(timetbl_)operator([)ident(RaceTime)operator(::)ident(FINISH_TIME)operator(]) + operator(<<) string<delimiter(")char(\\n)content(incr_time: )delimiter(")> operator(<<) ident(r_)operator(.)ident(timetbl_)operator([)ident(RaceTime)operator(::)ident(INCR_TIME)operator(];) + + reserved(return) ident(out)operator(;) +operator(}) + +ident(TimeEntry)operator(::)ident(TimeEntry)operator(()pre_type(int) ident(value)operator(,) pre_type(char) ident(dim)operator(\)) operator(:) ident(value_)operator(()ident(value)operator(\),) ident(dim_)operator(()ident(dim)operator(\)) operator({}) + +pre_type(bool) ident(TimeEntry)operator(::)directive(operator)operator(<()directive(const) ident(TimeEntry)operator(&) ident(right)operator(\)) directive(const) +operator({) + reserved(return) operator(()ident(dim_) operator(==) ident(right)operator(.)ident(dim_)operator(\)) operator(?) operator(()ident(value_) operator(<) ident(right)operator(.)ident(value_)operator(\)) operator(:) operator(!()ident(dim_) operator(<) ident(right)operator(.)ident(dim_)operator(\);) +operator(}) + +ident(RaceTime)operator(::)ident(RaceTime)operator((\)) +operator({) + ident(timetbl_)operator(.)ident(insert)operator(()ident(TENTRY)operator(()ident(START_TIME)operator(,) ident(TimeEntry)operator(()integer(0)operator(,) char('s')operator(\)\)\);) + ident(timetbl_)operator(.)ident(insert)operator(()ident(TENTRY)operator(()ident(FINISH_TIME)operator(,) ident(TimeEntry)operator(()integer(0)operator(,) char('s')operator(\)\)\);) + ident(timetbl_)operator(.)ident(insert)operator(()ident(TENTRY)operator(()ident(INCR_TIME)operator(,) ident(TimeEntry)operator(()integer(0)operator(,) char('s')operator(\)\)\);) +operator(}) + +ident(RaceTime)operator(&) ident(RaceTime)operator(::)ident(start_time)operator(()directive(const) ident(TimeEntry)operator(&) ident(time)operator(\)) +operator({) + ident(timetbl_)operator([)ident(START_TIME)operator(]) operator(=) ident(time)operator(;) reserved(return) operator(*)local_variable(this)operator(;) +operator(}) + +ident(RaceTime)operator(&) ident(RaceTime)operator(::)ident(finish_time)operator(()directive(const) ident(TimeEntry)operator(&) ident(time)operator(\)) +operator({) + ident(timetbl_)operator([)ident(FINISH_TIME)operator(]) operator(=) ident(time)operator(;) reserved(return) operator(*)local_variable(this)operator(;) +operator(}) + +ident(RaceTime)operator(&) ident(RaceTime)operator(::)ident(incr_time)operator(()directive(const) ident(TimeEntry)operator(&) ident(time)operator(\)) +operator({) + ident(timetbl_)operator([)ident(INCR_TIME)operator(]) operator(=) ident(time)operator(;) reserved(return) operator(*)local_variable(this)operator(;) +operator(}) + +directive(void) ident(the_func)operator(()directive(const) ident(RaceTime)operator(&) ident(r)operator(\)) +operator({) + ident(std)operator(::)ident(cout) operator(<<) ident(r) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ----------------------------) + +comment(// The Boost 'parameter' library requires a significant amount of setup code to be written,) +comment(// much more than this section warrants. My recommendation is to read carefully through the) +comment(// tutorial to determine whether a problem for which it is being considered justifies all) +comment(// the setup.) + +comment(// @@PLEAC@@_10.8) +comment(// The Boost 'tuple' Library also allows multiple assignment to variables, including the) +comment(// selective skipping of return values) + +preprocessor(#include) include(<iostream>) + +preprocessor(#include) include(<boost/tuple/tuple.hpp>) + +reserved(typedef) ident(boost)operator(::)ident(tuple)operator(<)pre_type(int)operator(,) pre_type(int)operator(,) pre_type(int)operator(>) ident(T3)operator(;) + +ident(T3) ident(func)operator((\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + pre_type(int) ident(a) operator(=) integer(6)operator(,) ident(b) operator(=) integer(7)operator(,) ident(c) operator(=) integer(8)operator(;) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) char(',') operator(<<) ident(b) operator(<<) char(',') operator(<<) ident(c) operator(<<) ident(std)operator(::)ident(endl)operator(;) + + comment(// A tuple of references to the referred variables is created; the values) + comment(// captured from the returned tuple are thus multiply-assigned to them) + ident(boost)operator(::)ident(tie)operator(()ident(a)operator(,) ident(b)operator(,) ident(c)operator(\)) operator(=) ident(func)operator((\);) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) char(',') operator(<<) ident(b) operator(<<) char(',') operator(<<) ident(c) operator(<<) ident(std)operator(::)ident(endl)operator(;) + + comment(// Variables can still be individually referenced) + ident(a) operator(=) integer(11)operator(;) ident(b) operator(=) integer(23)operator(;) ident(c) operator(=) integer(56)operator(;) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) char(',') operator(<<) ident(b) operator(<<) char(',') operator(<<) ident(c) operator(<<) ident(std)operator(::)ident(endl)operator(;) + + comment(// Return values may be ignored; affected variables retain existing values) + ident(boost)operator(::)ident(tie)operator(()ident(a)operator(,) ident(boost)operator(::)ident(tuples)operator(::)ident(ignore)operator(,) ident(c)operator(\)) operator(=) ident(func)operator((\);) + ident(std)operator(::)ident(cout) operator(<<) ident(a) operator(<<) char(',') operator(<<) ident(b) operator(<<) char(',') operator(<<) ident(c) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ----) + +ident(T3) ident(func)operator((\)) operator({) reserved(return) ident(T3)operator(()integer(3)operator(,) integer(6)operator(,) integer(9)operator(\);) operator(}) + +comment(// @@PLEAC@@_10.9) +comment(// Like Standard C, C++ allows only the return of a single value. The return of multiple values) +comment(// *can*, however, be simulated by packaging them within an aggregate type [as in C], or a) +comment(// custom class, or one of the STL containers like std::vector. Probably the most robust, and) +comment(// [pseudo]-standardised, approach is to use the Boost 'tuple' Library, as will be done in this) +comment(// section. Notes:) +comment(// * Use made of Boost 'assign' Library to simplify container loading; this is a *very* handy) +comment(// library) +comment(// * Use made of Boost 'any' Library to make containers heterogenous; 'variant' Library is) +comment(// similar, and is more appropriate where type-safe container traversal is envisaged e.g.) +comment(// for printing ) + +preprocessor(#include) include(<string>) +preprocessor(#include) include(<vector>) +preprocessor(#include) include(<map>) + +preprocessor(#include) include(<boost/any.hpp>) +preprocessor(#include) include(<boost/tuple/tuple.hpp>) + +preprocessor(#include) include(<boost/assign/std/vector.hpp>) +preprocessor(#include) include(<boost/assign/list_inserter.hpp>) + +reserved(typedef) ident(std)operator(::)ident(vector)operator(<)ident(boost)operator(::)ident(any)operator(>) ident(ARRAY)operator(;) +reserved(typedef) ident(std)operator(::)ident(map)operator(<)ident(std)operator(::)pre_type(string)operator(,) ident(boost)operator(::)ident(any)operator(>) ident(HASH)operator(;) +reserved(typedef) ident(boost)operator(::)ident(tuple)operator(<)ident(ARRAY)operator(,) ident(HASH)operator(>) ident(ARRAY_HASH)operator(;) + +ident(ARRAY_HASH) ident(some_func)operator(()directive(const) ident(ARRAY)operator(&) ident(array)operator(,) directive(const) ident(HASH)operator(&) ident(hash)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Load containers using Boost 'assign' Library ) + directive(using) reserved(namespace) ident(boost)operator(::)ident(assign)operator(;) + ident(ARRAY) ident(array)operator(;) ident(array) operator(+=) integer(1)operator(,) integer(2)operator(,) integer(3)operator(,) integer(4)operator(,) integer(5)operator(;) + ident(HASH) ident(hash)operator(;) ident(insert)operator(()ident(hash)operator(\)) operator(()string<delimiter(")content(k1)delimiter(")>operator(,) integer(1)operator(\)) operator(()string<delimiter(")content(k2)delimiter(")>operator(,) integer(2)operator(\)) operator(()string<delimiter(")content(k3)delimiter(")>operator(,) integer(3)operator(\);) + + comment(// Pass arguments to 'somefunc' and retrieve them as members of a tuple) + ident(ARRAY_HASH) ident(refs) operator(=) ident(some_func)operator(()ident(array)operator(,) ident(hash)operator(\);) + + comment(// Retrieve copy of 'array' from tuple) + ident(ARRAY) ident(ret_array) operator(=) ident(boost)operator(::)ident(get)operator(<)integer(0)operator(>()ident(refs)operator(\);) + + comment(// Retrieve copy of 'hash' from tuple) + ident(HASH) ident(ret_hash) operator(=) ident(boost)operator(::)ident(get)operator(<)integer(1)operator(>()ident(refs)operator(\);) +operator(}) + +comment(// ----) + +ident(ARRAY_HASH) ident(some_func)operator(()directive(const) ident(ARRAY)operator(&) ident(array)operator(,) directive(const) ident(HASH)operator(&) ident(hash)operator(\)) +operator({) + operator(;) comment(// ... do something with 'array' and 'hash') + + reserved(return) ident(ARRAY_HASH)operator(()ident(array)operator(,) ident(hash)operator(\);) +operator(}) + +comment(// @@PLEAC@@_10.10) +comment(// Like function calls in Standard C, function calls in C++ need to conform to signature) +comment(// requirements; a function call must match its declaration with the same number, and type,) +comment(// of arguments passed [includes implicitly-passed default arguments], and the same return) +comment(// value type. Thus, unlike Perl, a function declared to return a value *must* do so, thus) +comment(// cannot 'return nothing' to indicate failure. ) +comment(// Whilst in Standard C certain conventions like returning NULL pointers, or returning -1, to) +comment(// indicate the 'failure' of a task [i.e. function return codes are checked, and control) +comment(// proceeds conditionally] are used, Standard C++ sports facilities which lessen the need for) +comment(// dong the same. Specifically, C++ offers:) +comment(// * Built-in exception handling which can be used to detect [and perhaps recover from],) +comment(// all manner of unusual, or erroneous / problematic situations. One recommended use is) +comment(// to avoid writing code that performs a lot of return code checking) +comment(// * Native OOP support allows use of the Null Object Design Pattern. Put simply, rather than) +comment(// than checking return codes then deciding on an action, an object with some predefined) +comment(// default behaviour is returned / used where an unusual / erroneous / problematic situation) +comment(// is encountered. This approach could be as simple as having some sort of default base) +comment(// class member function behaviour, or as complex as having a diagnostic-laden object created) +comment(// * Functions can still return 'error-indicating entities', but rather than primitive types) +comment(// like 'int's or NULL pointers, complex objects can be returned. For example, the Boost) +comment(// Library sports a number of such types:) +comment(// - 'tuple') +comment(// - 'any', 'variant' and 'optional') +comment(// - 'tribool' [true, false, indeterminate]) + +comment(// Exception Handling Example) + +reserved(class) class(XYZ_exception) operator({};) + +pre_type(int) ident(func)operator((\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + pre_type(int) ident(valid_value) operator(=) integer(0)operator(;) + + reserved(try) + operator({) + operator(;) comment(// ...) + + ident(valid_value) operator(=) ident(func)operator((\);) + + operator(;) comment(// ...) + operator(}) + + reserved(catch)operator(()directive(const) ident(XYZ_exception)operator(&) ident(e)operator(\)) + operator({) + operator(;) comment(// ...) + operator(}) +operator(}) + +comment(// ----) + +pre_type(int) ident(func)operator((\)) +operator({) + pre_type(bool) ident(error_detected) operator(=) pre_constant(false)operator(;) + pre_type(int) ident(valid_value)operator(;) + + operator(;) comment(// ...) + + reserved(if) operator(()ident(error_detected)operator(\)) reserved(throw) ident(XYZ_exception)operator((\);) + + operator(;) comment(// ...) + + reserved(return) ident(valid_value)operator(;) +operator(}) + +comment(// ------------) + +comment(// Null Object Design Pattern Example) + +preprocessor(#include) include(<iostream>) + +reserved(class) class(Value) +operator({) +directive(public)operator(:) + directive(virtual) directive(void) ident(do_something)operator((\)) operator(=) integer(0)operator(;) +operator(};) + +reserved(class) class(NullValue) operator(:) directive(public) ident(Value) +operator({) +directive(public)operator(:) + directive(virtual) directive(void) ident(do_something)operator((\);) +operator(};) + +reserved(class) class(ValidValue) operator(:) directive(public) ident(Value) +operator({) +directive(public)operator(:) + directive(virtual) directive(void) ident(do_something)operator((\);) +operator(};) + +ident(Value)operator(*) ident(func)operator((\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Error checking is performed within 'func'. However, regardless of the outcome, an) + comment(// object of 'Value' type is returned which possesses similar behaviour, though appropriate) + comment(// to whether processing was successful or not. In this way no error checking is needed) + comment(// outside of 'func') + ident(Value)operator(*) ident(v) operator(=) ident(func)operator((\);) + + ident(v)operator(->)ident(do_something)operator((\);) + + reserved(delete) ident(v)operator(;) +operator(}) + +comment(// ----) + +directive(void) ident(NullValue)operator(::)ident(do_something)operator((\)) +operator({) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(*null*)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +directive(void) ident(ValidValue)operator(::)ident(do_something)operator((\)) +operator({) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(valid)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +ident(Value)operator(*) ident(func)operator((\)) +operator({) + pre_type(bool) ident(error_detected) operator(=) pre_constant(true)operator(;) + + operator(;) comment(// ...) + + reserved(if) operator(()ident(error_detected)operator(\)) reserved(return) reserved(new) ident(NullValue)operator(;) + + operator(;) comment(// ...) + + reserved(return) reserved(new) ident(ValidValue)operator(;) +operator(}) + +comment(// ----------------------------) + +comment(// The Boost 'optional' library has many uses, but in the current context, one is of particular) +comment(// use: returning a specified type [thus satisfying language requirements], but whose value) +comment(// may be 'set' [if the function succeeded] or 'unset' [if it failed], and this condition very) +comment(// easily checked) + +preprocessor(#include) include(<iostream>) + +preprocessor(#include) include(<cstdlib>) + +preprocessor(#include) include(<string>) +preprocessor(#include) include(<vector>) +preprocessor(#include) include(<map>) + +preprocessor(#include) include(<boost/optional/optional.hpp>) + +reserved(class) class(func_fail) +operator({) +directive(public)operator(:) + directive(explicit) ident(func_fail)operator(()directive(const) ident(std)operator(::)pre_type(string)operator(&) ident(msg)operator(\)) operator(:) ident(msg_)operator(()ident(msg)operator(\)) operator({}) + directive(const) ident(std)operator(::)pre_type(string)operator(&) ident(msg)operator((\)) directive(const) operator({) reserved(return) ident(msg_)operator(;) operator(}) +directive(private)operator(:) + directive(const) ident(std)operator(::)pre_type(string) ident(msg_)operator(;) +operator(};) + +comment(// ----) + +directive(void) ident(die)operator(()directive(const) ident(std)operator(::)pre_type(string)operator(&) ident(msg)operator(\);) + +ident(boost)operator(::)ident(optional)operator(<)pre_type(int)operator(>) ident(sfunc)operator((\);) +ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) operator(>) ident(afunc)operator((\);) +ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(map)operator(<)ident(std)operator(::)pre_type(string)operator(,) pre_type(int)operator(>) operator(>) ident(hfunc)operator((\);) + +comment(// ------------) + +pre_type(int) ident(main)operator((\)) +operator({) + reserved(try) + operator({) + ident(boost)operator(::)ident(optional)operator(<)pre_type(int)operator(>) ident(s)operator(;) + ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) operator(>) ident(a)operator(;) + ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(map)operator(<)ident(std)operator(::)pre_type(string)operator(,) pre_type(int)operator(>) operator(>) ident(h)operator(;) + + reserved(if) operator((!()ident(s) operator(=) ident(sfunc)operator((\)\)\)) reserved(throw) ident(func_fail)operator(()string<delimiter(")content('sfunc' failed)delimiter(")>operator(\);) + reserved(if) operator((!()ident(a) operator(=) ident(afunc)operator((\)\)\)) reserved(throw) ident(func_fail)operator(()string<delimiter(")content('afunc' failed)delimiter(")>operator(\);) + reserved(if) operator((!()ident(h) operator(=) ident(hfunc)operator((\)\)\)) reserved(throw) ident(func_fail)operator(()string<delimiter(")content('hfunc' failed)delimiter(")>operator(\);) + + operator(;) comment(// ... do stuff with 's', 'a', and 'h' ...) + pre_type(int) ident(scalar) operator(=) operator(*)ident(s)operator(;) + + operator(;) comment(// ...) + operator(}) + + reserved(catch) operator(()directive(const) ident(func_fail)operator(&) ident(e)operator(\)) + operator({) + ident(die)operator(()ident(e)operator(.)ident(msg)operator((\)\);) + operator(}) + + operator(;) comment(// ... other code executed if no error above ...) +operator(}) + +comment(// ------------) + +directive(void) ident(die)operator(()directive(const) ident(std)operator(::)pre_type(string)operator(&) ident(msg)operator(\)) +operator({) + ident(std)operator(::)ident(cerr) operator(<<) ident(msg) operator(<<) ident(std)operator(::)ident(endl)operator(;) + + comment(// Should only be used if all objects in the originating local scope have been destroyed) + ident(std)operator(::)ident(exit)operator(()ident(EXIT_FAILURE)operator(\);) +operator(}) + +comment(// ----) + +ident(boost)operator(::)ident(optional)operator(<)pre_type(int)operator(>) ident(sfunc)operator((\)) +operator({) + pre_type(bool) ident(error_detected) operator(=) pre_constant(true)operator(;) + + pre_type(int) ident(valid_int_value)operator(;) + + operator(;) comment(// ...) + + reserved(if) operator(()ident(error_detected)operator(\)) reserved(return) ident(boost)operator(::)ident(optional)operator(<)pre_type(int)operator(>(\);) + + operator(;) comment(// ...) + + reserved(return) ident(boost)operator(::)ident(optional)operator(<)pre_type(int)operator(>()ident(valid_int_value)operator(\);) +operator(}) + +ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) operator(>) ident(afunc)operator((\)) +operator({) + comment(// ... code not shown ...) + + reserved(return) ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) operator(>(\);) + + comment(// ... code not shown) +operator(}) + +ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(map)operator(<)ident(std)operator(::)pre_type(string)operator(,) pre_type(int)operator(>) operator(>) ident(hfunc)operator((\)) +operator({) + comment(// ... code not shown ...) + + reserved(return) ident(boost)operator(::)ident(optional)operator(<) ident(std)operator(::)ident(map)operator(<)ident(std)operator(::)pre_type(string)operator(,) pre_type(int)operator(>) operator(>(\);) + + comment(// ... code not shown ...) +operator(}) + +comment(// @@PLEAC@@_10.11) +comment(// Whilst in Perl function prototyping is optional, this is not the case in C++, where it is) +comment(// necessary to:) +comment(// * Declare a function before use; this could either be a function declaration separate from) +comment(// the function definition, or the function definition itself which serves as its own) +comment(// declaration) +comment(// * Specify both parameter positional and type information; parameter names are optional in) +comment(// declarations, mandatory in definitions) +comment(// * Specify return type) + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<vector>) + +comment(// Function Declaration) +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(myfunc)operator(()pre_type(int) ident(arg1)operator(,) pre_type(int) ident(arg2)operator(\);) comment(// Also possible: std::vector<int> myfunc(int, int\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Call function with all required arguments; this is the only calling method) + comment(// [except for calling via function pointer which still needs all arguments supplied]) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(results) operator(=) ident(myfunc)operator(()integer(3)operator(,) integer(5)operator(\);) + + comment(// Let's look at our return array's contents) + ident(std)operator(::)ident(cout) operator(<<) ident(results)operator([)integer(0)operator(]) operator(<<) char(':') operator(<<) ident(results)operator([)integer(1)operator(]) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ----) + +comment(// Function Definition) +ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(myfunc)operator(()pre_type(int) ident(arg1)operator(,) pre_type(int) ident(arg2)operator(\)) +operator({) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(r)operator(;) + + ident(std)operator(::)ident(back_inserter)operator(()ident(r)operator(\)) operator(=) ident(arg1)operator(;) + ident(std)operator(::)ident(back_inserter)operator(()ident(r)operator(\)) operator(=) ident(arg2)operator(;) + + reserved(return) ident(r)operator(;) +operator(}) + +comment(// ------------) + +comment(// A version on the above code that is generic, that is, making use of the C++ template) +comment(// mechanism to work with any type) + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<vector>) + +comment(// Function Declaration) +reserved(template) operator(<)reserved(class) class(T)operator(>) ident(std)operator(::)ident(vector)operator(<)ident(T)operator(>) ident(myfunc)operator(()directive(const) ident(T)operator(&) ident(arg1)operator(,) directive(const) ident(T)operator(&) ident(arg2)operator(\);) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + ident(std)operator(::)ident(vector)operator(<)pre_type(int)operator(>) ident(results) operator(=) ident(myfunc)operator(()integer(3)operator(,) integer(5)operator(\);) + + ident(std)operator(::)ident(cout) operator(<<) ident(results)operator([)integer(0)operator(]) operator(<<) char(':') operator(<<) ident(results)operator([)integer(1)operator(]) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ----) + +comment(// Function Definition) +reserved(template) operator(<)reserved(class) class(T)operator(>) ident(std)operator(::)ident(vector)operator(<)ident(T)operator(>) ident(myfunc)operator(()directive(const) ident(T)operator(&) ident(arg1)operator(,) directive(const) ident(T)operator(&) ident(arg2)operator(\)) +operator({) + ident(std)operator(::)ident(vector)operator(<)ident(T)operator(>) ident(r)operator(;) + + ident(std)operator(::)ident(back_inserter)operator(()ident(r)operator(\)) operator(=) ident(arg1)operator(;) + ident(std)operator(::)ident(back_inserter)operator(()ident(r)operator(\)) operator(=) ident(arg2)operator(;) + + reserved(return) ident(r)operator(;) +operator(}) + +comment(// ------------) + +comment(// Other Perl examples are omitted since there is no variation in C++ function calling or) +comment(// parameter handling) + +comment(// @@PLEAC@@_10.12) +comment(// One of the key, non-object oriented features of Standard C++ is its built-in support for) +comment(// exceptions / exception handling. The feature is well-integrated into the language, including) +comment(// a set of predefined exception classes included in, and used by, the Standard Library, is) +comment(// quite easy to use, and helps the programmer write robust code provided certain conventions) +comment(// are followed. On the downside, the C++ exception handling system is criticised for imposing) +comment(// significant runtime overhead, as well as increasing executable code size [though this) +comment(// varies considerably between CPU's, OS's, and compilers]. Please refer to the corresponding) +comment(// section in PLEAC-C/Posix/GNU for pertinent reading references.) +comment(//) +comment(// The example code below matches the PLEAC-C/Posix/GNU example rather than the Perl code. Note:) +comment(// * A very minimal, custom exception class is implemented; a more complex class, one richer in) +comment(// diagnostic information, could have been implemented, or perhaps one based on a standard) +comment(// exception class like 'std::exception') +comment(// * Ordinarily error / exception messages are directed to 'std::cerr' or 'std::clog') +comment(// * General recommendation is to throw 'temporaries' [via invoking a constructor],) +comment(// and to 'catch' as const reference(s\)) +comment(// * Proper 'cleanup' is very important; consult a suitable book for guidance on writing) +comment(// 'exception safe' code) + +preprocessor(#include) include(<iostream>) +preprocessor(#include) include(<string>) + +reserved(class) class(FullmoonException) +operator({) +directive(public)operator(:) + directive(explicit) ident(FullmoonException)operator(()directive(const) ident(std)operator(::)pre_type(string)operator(&) ident(msg)operator(\)) operator(:) ident(msg_)operator(()ident(msg)operator(\)) operator({}) + + directive(friend) ident(std)operator(::)ident(ostream)operator(&) directive(operator)operator(<<()ident(std)operator(::)ident(ostream)operator(&) ident(out)operator(,) directive(const) ident(FullmoonException)operator(&) ident(e)operator(\)) + operator({) + ident(out) operator(<<) ident(e)operator(.)ident(msg_)operator(;) reserved(return) ident(out)operator(;) + operator(}) +directive(private)operator(:) + directive(const) ident(std)operator(::)pre_type(string) ident(msg_)operator(;) +operator(};) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(main - entry)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) + + reserved(try) + operator({) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(try block - entry)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(... doing stuff ...)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) + + comment(// if (... error condition detected ...\)) + reserved(throw) ident(FullmoonException)operator(()string<delimiter(")content(... the problem description ...)delimiter(")>operator(\);) + + comment(// Control never gets here ...) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(try block - end)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) + operator(}) + + reserved(catch)operator(()directive(const) ident(FullmoonException)operator(&) ident(e)operator(\)) + operator({) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(Caught a'Fullmoon' exception. Message: )delimiter(")> + operator(<<) string<delimiter(")content([)delimiter(")> operator(<<) ident(e) operator(<<) string<delimiter(")content(])delimiter(")> + operator(<<) ident(std)operator(::)ident(endl)operator(;) + operator(}) + + reserved(catch)operator(()operator(.)operator(.)operator(.)operator(\)) + operator({) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(Caught an unknown exceptione)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) + operator(}) + + comment(// Control gets here regardless of whether an exception is thrown or not) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(main - end)delimiter(")> operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// @@PLEAC@@_10.13) +comment(// Standard C++ sports a namespace facility which allows an application to be divided into) +comment(// logical sub-systems, each of which operates within its own scope. Put very simply, the same) +comment(// identifiers [i.e. name of types, objects, and functions] may be each used in a namespace) +comment(// without fear of a nameclash occurring when logical sub-systems are variously combined as) +comment(// an application. The name-clash problem is inherent in single-namespace languages like C; it) +comment(// often occurs when several third-party libraries are used [a common occurrence in C], or) +comment(// when an application scales up. The remedy is to rename identifiers, or, in the case of ) +comment(// functions that cannot be renamed, to wrap them up in other functions in a separate source) +comment(// file. Of course the problem may be minimised via strict adherence to naming conventions. ) +comment(//) +comment(// The C++ namespace facility is important, too, because it avoids the need to utilise certain) +comment(// C language practices, in particular:) +comment(// * Use of, possibly, 'clumsy' naming conventions [as described above]) +comment(// * Partition an application by separating logically-related items into separate source) +comment(// files. Namespaces cross file boundaries, so items may reside in several source files) +comment(// and still comprise a single, logical sub-system) +comment(// * Anonymous namespaces avoid use of the 'static' keyword in creating file scope globals) + +comment(// Global variable) +pre_type(int) ident(age) operator(=) integer(18)operator(;) + +comment(// ----) + +directive(void) ident(print_age)operator((\)) +operator({) + comment(// Global value, 'age', is accessed) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(Age is )delimiter(")> operator(<<) ident(age) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ------------) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// A local variable named, 'age' will act to 'shadow' the globally) + comment(// defined version, thus any changes to, 'age', will not affect) + comment(// the global version) + pre_type(int) ident(age) operator(=) integer(5)operator(;) + + comment(// Prints 18, the current value of the global version) + ident(print_age)operator((\);) + + comment(// Local version is altered, *not* global version) + ident(age) operator(=) integer(23)operator(;) + + comment(// Prints 18, the current value of the global version) + ident(print_age)operator((\);) +operator(}) + +comment(// ----------------------------) + +comment(// Global variable) +pre_type(int) ident(age) operator(=) integer(18)operator(;) + +comment(// ----) + +directive(void) ident(print_age)operator((\)) +operator({) + comment(// Global value, 'age', is accessed) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(Age is )delimiter(")> operator(<<) ident(age) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ------------) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Here no local version declared: any changes affect global version) + ident(age) operator(=) integer(5)operator(;) + + comment(// Prints 5, the new value of the global version) + ident(print_age)operator((\);) + + comment(// Global version again altered) + ident(age) operator(=) integer(23)operator(;) + + comment(// Prints 23, the new value of the global version) + ident(print_age)operator((\);) +operator(}) + +comment(// ----------------------------) + +comment(// Global variable) +pre_type(int) ident(age) operator(=) integer(18)operator(;) + +comment(// ----) + +directive(void) ident(print_age)operator((\)) +operator({) + comment(// Global value, 'age', is accessed) + ident(std)operator(::)ident(cout) operator(<<) string<delimiter(")content(Age is )delimiter(")> operator(<<) ident(age) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ------------) + +pre_type(int) ident(main)operator((\)) +operator({) + comment(// Global version value saved into local version) + pre_type(int) ident(age) operator(=) operator(::)ident(age)operator(;) + + comment(// Prints 18, the new value of the global version) + ident(print_age)operator((\);) + + comment(// Global version this time altered) + operator(::)ident(age) operator(=) integer(23)operator(;) + + comment(// Prints 23, the new value of the global version) + ident(print_age)operator((\);) + + comment(// Global version value restored from saved local version) + operator(::)ident(age) operator(=) ident(age)operator(;) + + comment(// Prints 18, the restored value of the global version) + ident(print_age)operator((\);) +operator(}) + +comment(// @@PLEAC@@_10.14) +comment(// Please refer to the corresponding section in PLEAC-C/Posix/GNU since the points raised there) +comment(// about functions and function pointers apply equally to Standard C++ [briefly: functions) +comment(// cannot be redefined; several same-signature functions may be called via the same function) +comment(// pointer variable; code cannot be generated 'on-the-fly' (well, not without the use of) +comment(// several external tools, making it an extra-language, not integral, feature\)].) +comment(// @@INCOMPLETE@@) + +comment(// @@PLEAC@@_10.15) +comment(// Please refer to the corresponding section in PLEAC-C/Posix/GNU since all the points raised) +comment(// there apply equally to Standard C++ [briefly: undefined function calls are compiler-detected) +comment(// errors; function-pointer-based calls can't be checked for integrity].) +comment(// @@INCOMPLETE@@) + +comment(// @@PLEAC@@_10.16) +comment(// Standard C++ does not support either simple nested functions or closures, therefore the) +comment(// example cannot be implemented exactly as per the Perl code) + +comment(/* === +int outer(int arg\) +{ + int x = arg + 35; + + // *** wrong - illegal C++ *** + int inner(\) { return x * 19; } + + return x + inner(\); +} +=== */) + +comment(// The problem may, of course, be solved by defining two functions using parameter passing) +comment(// where appropriate, but this is contrary to the intent of the original Perl code) +pre_type(int) ident(inner)operator(()pre_type(int) ident(x)operator(\)) +operator({) + reserved(return) ident(x) operator(*) integer(19)operator(;) +operator(}) + +pre_type(int) ident(outer)operator(()pre_type(int) ident(arg)operator(\)) +operator({) + pre_type(int) ident(x) operator(=) ident(arg) operator(+) integer(35)operator(;) + reserved(return) ident(x) operator(+) ident(inner)operator(()ident(x)operator(\);) +operator(}) + +comment(// An arguably better [but far more complicated] approach is to encapsulate all items within) +comment(// a namespace, but again, is an approach that is counter the intent of the original Perl code) +preprocessor(#include) include(<iostream>) + +reserved(namespace) ident(nst) +operator({) + pre_type(int) ident(x)operator(;) + pre_type(int) ident(inner)operator((\);) + pre_type(int) ident(outer)operator(()pre_type(int) ident(arg)operator(\);) +operator(}) + +comment(// ----) + +pre_type(int) ident(main)operator((\)) +operator({) + ident(std)operator(::)ident(cout) operator(<<) ident(nst)operator(::)ident(outer)operator(()integer(3)operator(\)) operator(<<) ident(std)operator(::)ident(endl)operator(;) +operator(}) + +comment(// ----) + +pre_type(int) ident(nst)operator(::)ident(inner)operator((\)) +operator({) + reserved(return) ident(nst)operator(::)ident(x) operator(*) integer(19)operator(;) +operator(}) + +pre_type(int) ident(nst)operator(::)ident(outer)operator(()pre_type(int) ident(arg)operator(\)) +operator({) + ident(nst)operator(::)ident(x) operator(=) ident(arg) operator(+) integer(35)operator(;) + reserved(return) ident(nst)operator(::)ident(x) operator(+) ident(nst)operator(::)ident(inner)operator((\);) +operator(}) + +comment(// Another way to solve this problem and avoiding the use of an external function, is to) +comment(// create a local type and instantiate an object passing any required environment context) +comment(// to the constructor. Then, what appears as a parameterless nested function call, can be) +comment(// effected using 'operator(\)'. This approach most closely matches the original Perl code) + +pre_type(int) ident(outer)operator(()pre_type(int) ident(arg)operator(\)) +operator({) + pre_type(int) ident(x) operator(=) ident(arg) operator(+) integer(35)operator(;) + + comment(// 'Inner' is what is known as a Functor or Function Object [or Command Design Pattern]; it) + comment(// allows objects that capture state / context to be instantiated, and that state / context) + comment(// used / retained / altered at multiple future times. Both the STL and Boost Libraries) + comment(// provide extensive support these constructs) + reserved(struct) ident(Inner) + operator({) + pre_type(int) ident(n_)operator(;) + directive(explicit) ident(Inner)operator(()pre_type(int) ident(n)operator(\)) operator(:) ident(n_)operator(()ident(n)operator(\)) operator({}) + pre_type(int) directive(operator)operator((\)(\)) directive(const) operator({) reserved(return) ident(n_) operator(*) integer(19)operator(;) operator(}) + operator(}) ident(inner)operator(()ident(x)operator(\);) + + reserved(return) ident(x) operator(+) ident(inner)operator((\);) +operator(}) + +comment(// @@PLEAC@@_10.17) +comment(// @@INCOMPLETE@@) +comment(// @@INCOMPLETE@@) + diff --git a/test/scanners/cpp/pleac.in.cpp b/test/scanners/cpp/pleac.in.cpp new file mode 100644 index 0000000..5a09e8c --- /dev/null +++ b/test/scanners/cpp/pleac.in.cpp @@ -0,0 +1,2041 @@ +// -*- c++ -*-
+
+// @@PLEAC@@_NAME
+// @@SKIP@@ C++/STL/Boost
+
+
+// @@PLEAC@@_WEB
+// @@SKIP@@ http://www.research.att.com/~bs/C++.html
+// @@SKIP@@ http://www.boost.org/
+
+
+// @@PLEAC@@_1.0
+// NOTE: Whilst it is perfectly valid to use Standard C Library, or GNU
+// C Library, routines in C++ programs, the code examples here will, as
+// far as possible, avoid doing so, instead using C++-specific functionality
+// and idioms. In general:
+// * I/O will be iostream-based [i.e. no 'scanf', 'printf', 'fgets' etc]
+// * Container / iterator idioms based on the Standard Template Library [STL]
+// will replace the built-in array / raw pointer idioms typically used in C
+// * Boost Library functionality utilised wherever possible [the reason for
+// this is that much of this functionality is likely to appear in the next
+// C++ standard]
+// * Error detection/handling will generally be exception-based [this is done
+// to keep examples simple. Exception use is optional in C++, and is not as
+// pervasive as it is in other languages like Java or C#]
+// C-based solution(s) to problem(s) will be found in the corresponding section
+// of PLEAC-C/Posix/GNU.
+
+// In C++, one can use the builtin 'char *' type or the 'string' type
+// to represent strings. In this section, we will work with the C++
+// library 'string' class.
+
+// Characteristics of 'string' types:
+// - may be of any length
+// - are defined within the std namespace
+// - can be converted to a 'const char *' using std::string::c_str()
+// - can be subscripted to access individual characters (e.g., str[3]
+// returns the 4th character of the string
+// - memory associated with strings is reclaimed automatically as strings
+// go out of scope
+// - strings cannot be used as true/false values (i.e., the following is not
+// allowed: string s; if (s) {})
+
+//-----------------------------
+// Before using strings, you must include the <string> header file
+#include <string>
+
+//-----------------------------
+// To create a literal strings, you must use double quotes ("). You cannot
+// use single quotes.
+
+//-----------------------------
+// String variables must be declared -- if no value is given it's
+// value is the empty string ("").
+std::string s;
+
+//-----------------------------
+// To insert special characters, quote the character with \
+std::string s1 = "\\n"; // Two characters, \ and n
+std::string s2 = "Jon \"Maddog\" Orwant"; // Literal double quotes
+
+//-----------------------------
+// Strings can be declared in one of two ways
+std::string s1 = "assignment syntax";
+std::string s2("constructor syntax");
+
+//-----------------------------
+// Multi-line strings.
+// There is no equivalent to perl's "here" documents in c++
+std::string s1 = "
+This is a multiline string started and finished with double
+quotes that spans 4 lines (it contains 3 newline characters).
+";
+
+std::string s2 = "This is a multiline string started and finished with double
+quotes that spans 2 lines (it contains 1 newline character).";
+//-----------------------------
+
+
+// @@PLEAC@@_1.1
+std::string s = "some string";
+
+//-----------------------------
+std::string value1 = s.substr(offset, length);
+std::string value2 = s.substr(offset);
+
+// Unlike perl, the substr function returns a copy of the substring
+// rather than a reference to the existing substring, thus using substr
+// on the left hand side of an assignment statement will not modify
+// the original string. To get this functionality, you can use the
+// std::string::replace function.
+
+// Using offsets and lengths
+s.replace(offset, length, newstring);
+s.replace(offset, s.size()-offset, newtail);
+
+//-----------------------------
+// The C++ string class doesn't have anything equivalent to perl's unpack.
+// Instead, one can use C structures to import/export binary data
+
+//-----------------------------
+#include <string>
+string s = "This is what you have";
+
+std::string first = s.substr(0, 1); // "T"
+std::string second = s.substr(5, 2); // "is"
+std::string rest = s.substr(13); // "you have"
+
+// C++ strings do not support backwards indexing as perl does but
+// you can fake it out by subtracting the negative index from the
+// string length
+std::string last = s.substr(s.size()-1); // "e"
+std::string end = s.substr(s.size()-4); // "have"
+std::string piece = s.substr(s.size()-8, 3); // "you"
+
+//-----------------------------
+#include <string>
+#include <iostream>
+
+string s("This is what you have");
+std::cout << s << std::endl;
+// This is what you have
+
+s.replace(5,2,"wasn't"); // change "is to "wasn't"
+// This wasn't what you have
+
+s.replace(s.size()-12, 12, "ondrous"); // "This wasn't wondrous"
+// This wasn't wonderous
+
+s.replace(0, 1, ""); // delete first character
+// his wasn't wondrous
+
+s.replace(s.size()-10, 10, ""); // delete last 10 characters
+// his wasn'
+
+//-----------------------------
+// C++ does not have built-in support for the perl s///, m//, and tr///
+// operators; however, similar results can be achieved in at least
+// two ways:
+// - string operations such as string::find, string::rfind, etc.
+// - the boost regular expression library (regex++) supports perl
+// regular expression syntax.
+// TODO: Add examples of each.
+
+// MISSING: if (substr($string, -10) =~ /pattern/) {
+// print "Pattern matches in last 10 characters\n";
+// }
+
+// MISSING: substr($string, 0, 5) =~ s/is/at/g;
+
+//-----------------------------
+// exchange the first and last letters in a string using substr and replace
+string a = "make a hat";
+
+std::string first = a.substr(0,1);
+std::string last = a.substr(a.size()-1);
+
+a.replace(0,1, last);
+a.replace(a.size()-1, 1, first);
+
+// exchange the first and last letters in a string using indexing and swap
+#include <algorithm>
+std::swap(a[0], a[a.size()-1]);
+//-----------------------------
+
+
+// @@PLEAC@@_1.2
+//-----------------------------
+// C++ doesn't have functionality equivalent to the || and ||=.
+// If statements and trigraphs can be used instead.
+//-----------------------------
+// C++ doesn't have anything equivalent "defined". C++ variables
+// cannot be used at all if they have not previously been defined.
+
+//-----------------------------
+// Use b if b is not empty, else c
+a = b.size() ? b : c;
+
+// Set x to y unless x is not empty
+if (x.is_empty()) x = y;
+
+//-----------------------------
+foo = (!bar.is_empty()) ? bar : "DEFAULT VALUE";
+
+//-----------------------------
+// NOTE: argv is declared as char *argv[] in C/C++. We assume
+// the following code surrounds the following examples that deal
+// with argv. Also, arguments to a program start at argv[1] -- argv[0]
+// is the name of the executable that's running.
+#include <string.h>
+int main(int argc, char *argv[]) {
+ char **args = argv+1; // +1 skips argv[0], the name of the executable
+ // examples
+}
+
+//-----------------------------
+std::string dir = (*args) ? *argv++ : "/tmp";
+
+//-----------------------------
+std::string dir = argv[1] ? argv[1] : "/tmp";
+
+//-----------------------------
+std::string dir = (argc-1) ? argv[1] : "/tmp";
+
+//-----------------------------
+#include <map>
+std::map<std::string,int> count;
+
+count[shell.size() ? shell : "/bin/sh"]++;
+
+//-----------------------------
+// find the user name on Unix systems
+// TODO: Simplify. This is too ugly and complex
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#include "boost/lexical_cast.hpp"
+
+std::string user;
+char *msg = 0;
+passwd *pwd = 0;
+
+if ( (msg = getenv("USER")) ||
+ (msg = getenv("LOGNAME")) ||
+ (msg = getlogin()) )
+ user = msg;
+else if (pwd = getpwuid(getuid()))
+ user = pwd->pw_name;
+else
+ user = "Unknown uid number " + boost::lexical_cast<std::string>(getuid());
+
+//-----------------------------
+if (starting_point.is_empty()) starting_point = "Greenwich";
+
+//-----------------------------
+// Example using list. Other C++ STL containers work similarly.
+#include <list>
+list<int> a, b;
+if (a.is_empty()) a = b; // copy only if a is empty
+a = (!b.is_empty()) ? b : c; // asign b if b nonempty, else c
+//-----------------------------
+
+
+// @@PLEAC@@_1.3
+//-----------------------------
+#include <algorithm>
+std::swap(a, b);
+
+//-----------------------------
+temp = a;
+a = b;
+b = temp;
+
+//-----------------------------
+std::string a("alpha");
+std::string b("omega");
+std::swap(a,b);
+
+//-----------------------------
+// The ability to exchange more than two variables at once is not
+// built into the C++ language or C++ standard libraries. However, you
+// can use the boost tuple library to accomplish this.
+#include <boost/tuple/tuple.hpp>
+
+boost::tie(alpha,beta,production)
+ = boost::make_tuple("January", "March", "August");
+// move beta to alpha,
+// move production to beta,
+// move alpha to production
+boost::tie(alpha, beta, production)
+ = boost::make_tuple(beta, production, alpha);
+//-----------------------------
+
+
+// @@PLEAC@@_1.4
+//-----------------------------
+// There are several ways to convert between characters
+// and integers. The examples assume the following declarations:
+char ch;
+int num;
+
+//-----------------------------
+// Using implicit conversion
+num = ch;
+ch = num;
+
+//-----------------------------
+// New-style C++ casts
+ch = static_cast<char>(num);
+num = static_cast<int>(ch);
+
+//-----------------------------
+// Old-style C casts
+ch = (char)num;
+num = (int)ch;
+
+//-----------------------------
+// Using the C++ stringstream class
+#include <sstream> // On some older compilers, use <strstream>
+std::stringstream a; // On some older compilers, use std::strstream
+
+a << ch; // Append character to a string
+a >> num; // Output character as a number
+
+a << num; // Append number to a string
+a >> ch; // Output number as a character
+
+//-----------------------------
+// Using sprintf, printf
+char str[2]; // Has to be length 2 to have room for NULL character
+sprintf(str, "%c", num);
+printf("Number %d is character %c\n", num, num);
+
+//-----------------------------
+int ascii_value = 'e'; // now 101
+char character = 101; // now 'e'
+
+//-----------------------------
+printf("Number %d is character %c\n", 101, 101);
+
+//-----------------------------
+// Convert from HAL to IBM, character by character
+#include <string>
+#include <iostream>
+
+std::string ibm, hal = "HAL";
+for (unsigned int i=0; i<hal.size(); ++i)
+ ibm += hal[i]+1; // Add one to each ascii value
+std::cout << ibm << std::endl; // prints "IBM"
+
+//-----------------------------
+// Convert hal from HAL to IBM
+#include <string>
+#include <iostream>
+#include <functional> // For bind1st and plus<>
+#include <algorithm> // For transform
+
+std::string hal = "HAL";
+transform(hal.begin(), hal.end(), hal.begin(),
+ bind1st(plus<char>(),1));
+std::cout << hal << std::endl; // prints "IBM"
+//-----------------------------
+
+
+// @@PLEAC@@_1.5
+//-----------------------------
+// Since C++ strings can be accessed one character at a time,
+// there's no need to do any processing on the string to convert
+// it into an array of characters.
+#include <string>
+std::string s;
+
+// Accessing characters using for loop and integer offsets
+for (unsigned int i=0; i<s.size(); ++i) {
+ // do something with s[i]
+}
+
+// Accessing characters using iterators
+for (std::string::iterator i=s.begin(); i!=s.end(); ++i) {
+ // do something with *i
+}
+
+//-----------------------------
+std::string str = "an apple a day";
+std::map<char,int> seen;
+
+for (std::string::iterator i=str.begin(); i!=str.end(); ++i)
+ seen[*i]++;
+
+std::cout << "unique chars are: ";
+for (std::map<char,int>::iterator i=seen.begin(); i!=seen.end(); ++i)
+ std::cout << i->first;
+std::cout << std::endl;
+// unique chars are: adelnpy
+
+//-----------------------------
+int sum = 0;
+for (std::string::iterator i=str.begin(); i!=str.end(); ++i)
+ sum += *i;
+std::cout << "sum is " << sum << std::endl;
+// prints "sum is 1248" if str was "an appla a day"
+
+
+//-----------------------------
+// MISSING: sysv-like checksum program
+
+//-----------------------------
+// slowcat, emulate a slow line printer
+#include <sys/time.h>
+#include <iostream>
+#include <fstream>
+
+int main(int argc, char *argv[]) {
+ timeval delay = { 0, 50000 }; // Delay in { seconds, nanoseconds }
+ char **arg = argv+1;
+ while (*arg) { // For each file
+ std::ifstream file(*arg++);
+ char c;
+ while (file.get(c)) {
+ std::cout.put(c);
+ std::cout.flush();
+ select(0, 0, 0, 0, &delay);
+ }
+ }
+}
+//-----------------------------
+
+
+// @@PLEAC@@_1.6
+//-----------------------------
+#include <string>
+#include <algorithm> // For reverse
+std::string s;
+
+reverse(s.begin(), s.end());
+
+//-----------------------------
+#include <vector> // For std::vector
+#include <sstream> // On older compilers, use <strstream>
+#include "boost/regex.hpp" // For boost::regex_split
+
+std::string str;
+std::vector<std::string> words;
+boost::regex_split(std::back_inserter(words), str);
+reverse(words.begin(), words.end()); // Reverse the order of the words
+
+std::stringstream revwords; // On older compilers, use strstream
+copy(words.begin(), words.end(), ostream_inserter<string>(revwords," ");
+std::cout << revwards.str() << std::endl;
+
+//-----------------------------
+std::string rts = str;
+reverse(rts.begin(), rts.end()); // Reverses letters in rts
+
+//-----------------------------
+std::vector<string> words;
+reverse(words.begin(), words.end()); // Reverses words in container
+
+//-----------------------------
+// Reverse word order
+std::string s = "Yoda said, 'can you see this?'";
+
+std::vector<std::string> allwords;
+boost::regex_split(std::back_inserter(allwords), s);
+
+reverse(allwords.begin(), allwords.end());
+
+std::stringstream revwords; // On older compilers, use strstream
+copy(allwords.begin(), allwords.end(), ostream_inserter<string>(revwords," "));
+std::cout << revwards.str() << std::endl;
+// this?' see you 'can said, Yoda
+
+//-----------------------------
+std::string word = "reviver";
+bool is_palindrome = equal(word.begin(), word.end(), word.rbegin());
+
+//-----------------------------
+#include <ifstream>
+
+std::ifstream dict("/usr/dict/words");
+std::string word;
+while(getline(dict,word)) {
+ if (equal(word.begin(), word.end(), word.rbegin()) &&
+ word.size() > 5)
+ std::cout << word << std::endl;
+}
+//-----------------------------
+
+
+// @@PLEAC@@_1.7
+//-----------------------------
+#include <string>
+
+std::string::size_type pos;
+while ((pos = str.find("\t")) != std::string::npos)
+ str.replace(pos, 1, string(' ',8-pos%8));
+//-----------------------------
+
+
+// @@PLEAC@@_1.8
+//-----------------------------
+// Not applicable to C++
+//-----------------------------
+
+
+// @@PLEAC@@_1.9
+//-----------------------------
+// TODO: Fix to be more like cookbook
+// TODO: Modify/add code to do this with locales
+#include <string>
+#include <algorithm>
+
+std::string phrase = "bo peep";
+transform(phrase.begin(), phrase.end(), phrase.begin(), toupper);
+// "BO PEEP"
+transform(phrase.begin(), phrase.end(), phrase.begin(), tolower);
+// "bo peep"
+//-----------------------------
+
+
+// @@PLEAC@@_1.10
+//-----------------------------
+// C++ does not provide support for perl-like in-string interpolation,
+// concatenation must be used instead.
+
+#include <string>
+
+std::string var1, var2;
+std::string answer = var1 + func() + var2; // func returns string or char *
+
+//-----------------------------
+#include "boost/lexical_cast.hpp"
+
+int n = 4;
+std::string phrase = "I have " + boost::lexical_cast<string>(n+1) + " guanacos.";
+
+//-----------------------------
+std::cout << "I have " + boost::lexical_cast<string>(n+1) + " guanacos." << std::endl;
+
+
+// @@PLEAC@@_1.11
+//-----------------------------
+// C++ does not have "here documents".
+// TODO: Lots more.
+#include <string>
+#include "boost/regex.hpp"
+
+std::string var = "
+ your text
+ goes here.
+";
+
+boost::regex ex("^\\s+");
+var = boost::regex_merge(var, ex, "");
+
+// @@PLEAC@@_10.0
+// NOTE: Whilst it is perfectly valid to use Standard C Library, or GNU C Library, routines in
+// C++ programs, the code examples here will, as far as possible, avoid doing so, instead using
+// C++-specific functionality and idioms. In general:
+// * I/O will be iostream-based [i.e. no 'scanf', 'printf', 'fgets' etc]
+// * Container / iterator idioms based on the Standard Template Library [STL]
+// will replace the built-in array / raw pointer idioms typically used in C
+// * Boost Library functionality utilised wherever possible [the reason for
+// this is that much of this functionality is likely to appear in the next
+// C++ standard]
+// * Error detection/handling will generally be exception-based [this is done
+// to keep examples simple. Exception use is optional in C++, and is not as
+// pervasive as it is in other languages like Java or C#]
+// C-based solution(s) to problem(s) will be found in the corresponding section of PLEAC-C/Posix/GNU.
+
+#include <iostream>
+
+// 'greeted' defined outside of any namespace, class or function, so is part of the
+// global namespace, and will be visible throughout the entire executable. Should it
+// be necessary to restrict the visibility of this global identifier to the current
+// 'compilation unit' [i.e. current source file] then the following may be used:
+//
+// namespace { int greeted = 0; }
+//
+// The effect is similar to using the 'static' keyword, in this same context, in the C
+// language.
+
+int greeted = 0;
+
+int howManyGreetings();
+void hello();
+
+// ----
+
+int main()
+{
+ hello();
+
+ int greetings = howManyGreetings();
+
+ std::cout << "bye there!, there have been "
+ << greetings
+ << " greetings so far"
+ << std::endl;
+}
+
+// ----
+
+int howManyGreetings()
+{
+ // Access 'greeted' identifier in the global namespace using the scope resolution
+ // operator. Use of this operator is only necessary if a similarly-named identifier
+ // exists in a
+ return ::greeted;
+}
+
+void hello()
+{
+ // Here 'greeted' is accessed without additional qualification. Since a 'greeted' identifier
+ // exists only in the global namespace, it is that identifier that is used
+ std::cout << "high there!, this function has been called "
+ << ++greeted
+ << " times"
+ << std::endl;
+}
+
+// @@PLEAC@@_10.1
+// Standard C++ requires that a function be prototyped, hence the name and type of parameters
+// must be specified, and the argumemt list in any calls to that function must match the
+// parameter list, as shown here
+
+#include <cmath>
+
+double hypotenuse(double side1, double side2);
+
+// ----
+
+int main()
+{
+ double diag = hypotenuse(3.0, 4.0);
+}
+
+// ----
+
+double hypotenuse(double side1, double side2)
+{
+ return std::sqrt(std::pow(side1, 2.0) + std::pow(side2, 2.0));
+}
+
+// ----------------------------
+
+// Variable length argument list functions, via the C Language derived 'va_...' macros,
+// are also supported. However use of this facility is particularly discouraged in C++
+// because:
+// * It is an inherently type-unsafe facility; type safety is a core C++ concern
+// * Other facilities, such as overloaded functions, and default arguments [neither of which
+// are available in C] can sometimes obviate the need for variable length argument lists
+// * OOP techniques can also lessen the need for variable length argument lists. The most
+// obvious example here is the Iostream library where repeated calls of I/O operators replace
+// the format string / variable arguments of 'printf'
+
+#include <cmath>
+#include <cstdarg>
+
+double hypotenuse(double side1, ...);
+
+// ----
+
+int main()
+{
+ double diag = hypotenuse(3.0, 4.0);
+}
+
+// ----
+
+double hypotenuse(double side1, ...)
+{
+ // More details available in the corresponding section of PLEAC-C/Posix/GNU
+ va_list ap;
+ va_start(ap, side1);
+ double side2 = va_arg(ap, double);
+ va_end(ap);
+
+ return std::sqrt(std::pow(side1, 2.0) + std::pow(side2, 2.0));
+}
+
+// ----------------------------
+
+// An example using default arguments appears below
+
+#include <cmath>
+
+// Specify default argument values in declaration
+// Note: This may be done in either of the declaration or the definition [not both], but it
+// makes more sense to do so in the declaration since these are usually placed in header files
+// which may be included in several source files. The default argument values would need to be
+// known in all those locations
+double hypotenuse(double side1 = 3.0, double side2 = 4.0);
+
+// ----
+
+int main()
+{
+ // All arguments specified
+ double diag = hypotenuse(3.0, 4.0);
+
+ // Both calls utilise default argument value(s)
+ diag = hypotenuse(3.0);
+
+ diag = hypotenuse();
+}
+
+// ----
+
+double hypotenuse(double side1, double side2)
+{
+ return std::sqrt(std::pow(side1, 2.0) + std::pow(side2, 2.0));
+}
+
+// ----------------------------
+
+// A [very contrived, not very practical] example using function overloading appears below
+
+#include <cmath>
+
+double hypotenuse(double side1, double side2);
+double hypotenuse(double side1);
+double hypotenuse();
+
+// ----
+
+int main()
+{
+ // Call version (1)
+ double diag = hypotenuse(3.0, 4.0);
+
+ // Call version (2)
+ diag = hypotenuse(3.0);
+
+ // Call version (3)
+ diag = hypotenuse();
+}
+
+// ----
+
+// (1)
+double hypotenuse(double side1, double side2)
+{
+ return std::sqrt(std::pow(side1, 2.0) + std::pow(side2, 2.0));
+}
+
+// (2)
+double hypotenuse(double side1)
+{
+ return std::sqrt(std::pow(side1, 2.0) + std::pow(4.0, 2.0));
+}
+
+// (3)
+double hypotenuse()
+{
+ return std::sqrt(std::pow(3.0, 2.0) + std::pow(4.0, 2.0));
+}
+
+// ----------------------------
+
+#include <cstddef>
+#include <vector>
+
+std::vector<int> int_all(const double arr[], size_t arrsize);
+std::vector<int> int_all(const std::vector<double>& arr);
+
+// ----
+
+int main()
+{
+ // Load vectors from built-in arrays, or use Boost 'assign' library
+ const double nums[] = {1.4, 3.5, 6.7};
+ const size_t arrsize = sizeof(nums) / sizeof(nums[0]);
+
+ // Conversion effected at vector creation time
+ std::vector<int> ints = int_all(nums, arrsize);
+
+ // Vector -> vector copy / conversion
+ ints = int_all(std::vector<double>(nums, nums + arrsize));
+}
+
+// ----
+
+std::vector<int> int_all(const double arr[], size_t arrsize)
+{
+ return std::vector<int>(arr, arr + arrsize);
+}
+
+std::vector<int> int_all(const std::vector<double>& arr)
+{
+ std::vector<int> r;
+ r.assign(arr.begin(), arr.end()); // Type safe element copying
+ return r;
+}
+
+// ----------------------------
+
+#include <algorithm>
+#include <vector>
+
+#include <cmath>
+#include <cstddef>
+
+void trunc_em(std::vector<double>& arr);
+
+// ----
+
+int main()
+{
+ // Load vectors from built-in arrays, or use Boost 'assign' library
+ const double nums[] = {1.4, 3.5, 6.7};
+ const size_t arrsize = sizeof(nums) / sizeof(nums[0]);
+
+ std::vector<double> numsv(nums, nums + arrsize);
+
+ trunc_em(numsv);
+}
+
+// ----
+
+void trunc_em(std::vector<double>& arr)
+{
+ // Replace each element with the value returned by applying 'floor' to that element
+ std::transform(arr.begin(), arr.end(), arr.begin(), floor);
+}
+
+// @@PLEAC@@_10.2
+// Variables declared within a function body are local to that function, and those declared
+// outside a function body [and not as part of a class / struct definition, or enclosed within
+// a namespace] are global, that is, are visible throughout the executable unless their
+// visibility has been restricted to the source file in which they are defined via enclosing
+// them within an anonymous namespace [which has the same effect as using the 'static' keyword,
+// in this same context, in the C language]
+
+#include <vector>
+
+void somefunc()
+{
+ // All these variables are local to this function
+ int variable, another;
+
+ std::vector<int> vec(5);
+
+ ; // ...
+}
+
+// ----------------------------
+
+// A couple of generic, type-safe type conversion helpers. The Boost Library sports a conversion
+// library at: http://www.boost.org/libs/conversion/index.html
+
+#include <sstream>
+#include <string>
+
+class bad_conversion {};
+
+template<typename T> T fromString(const std::string& s)
+{
+ std::istringstream iss(s);
+ T t; iss >> t;
+ if (!iss) throw bad_conversion();
+ return t;
+}
+
+template<typename T> std::string toString(const T& t)
+{
+ std::ostringstream oss;
+ oss << t << std::ends;
+ if (!oss) throw bad_conversion();
+ return std::string(oss.str());
+}
+
+// ------------
+
+#include <string>
+
+// File scope variables
+namespace
+{
+ std::string name;
+ int age, c, condition;
+}
+
+void run_check();
+void check_x(int x);
+
+// ----
+
+// An alternative, C++-specific approach, to command-line handling and type conversion
+// may be seen at: http://www.boost.org/libs/conversion/lexical_cast.htm
+
+int main(int argc, char* argv[])
+{
+ name.assign(argv[1]);
+
+ try
+ {
+ age = fromString<int>(argv[2]);
+ }
+
+ catch (const bad_conversion& e)
+ {
+ ; // ... handle conversion error ...
+ }
+
+ check_x(age);
+}
+
+// ------------
+
+void run_check()
+{
+ // Full access to file scope variables
+ condition = 1;
+ // ...
+}
+
+void check_x(int x)
+{
+ // Full access to file scope variables
+ std::string y("whatever");
+
+ run_check();
+
+ // 'condition' updated by 'run_check'
+ if (condition)
+ {
+ ; // ...
+ }
+}
+
+// @@PLEAC@@_10.3
+// Standard C++, owing to its C heritage, allows the creation of 'persistent private variables',
+// via use of the 'static' keyword. For more details about this, and illustrative code examples,
+// refer to this same section in PLEAC-C/Posix/GNU. Standard C++-specific methods of perfoming
+// this task involve use of the 'namespace' facility, or creating a class containing 'static'
+// members and using access specifiers to restrict access
+
+// This example replaces the 'static' keyword with use of an anonymous namespace to force
+// 'variable' to have file scope, and be visible only within the 'mysubs.cpp file. It is
+// therefore both persistant [because it is a global variable] and private [because it is
+// visible only to functions defined within the same source file]
+
+// File: 'mysubs.h'
+void mysub(void);
+void reset(void);
+
+// ----
+
+// File: 'mysubs.cpp'
+namespace
+{
+ int variable = 1;
+}
+
+void mysub(void)
+{
+ ; // ... do something with 'variable' ...
+}
+
+void reset(void) { variable = 1; }
+
+// ----
+
+// File: 'test.cpp'
+#include "mysubs.h"
+
+int main()
+{
+ // 'variable' is not accessable here
+
+ // Call 'mysub', which can access 'variable'
+ mysub();
+
+ // Call 'reset' which sets 'variable' to 1
+ reset();
+}
+
+// ------------
+
+// This example is similar to the previous one in using an anonymous namespace to restrict
+// variable visibility. It goes further, hoewever, grouping logically related items within
+// a named namespace, thus ensuring access to those items is controlled [i.e. requires
+// qualification, or a 'using' declaration or directive]
+
+// File: 'counter.h'
+namespace cnt
+{
+ int increment();
+ int decrement();
+}
+
+// ----
+
+// File: 'counter.cpp'
+namespace cnt
+{
+ // Ensures 'counter' is visible only within the current source file
+ namespace { int counter = 0; }
+
+ void reset(int v = 0) { counter = v; }
+
+ int increment() { return ++counter; }
+ int decrement() { return --counter; }
+}
+
+// ----
+
+// File: 'test.cpp'
+#include <iostream>
+#include "counter.h"
+
+int main()
+{
+ // Following line is illegal because 'cnt::counter' is private to the 'counter.cpp' file
+ // int c = cnt::counter;
+
+ int a = cnt::increment();
+ std::cout << a << std::endl;
+
+ a = cnt::decrement();
+ std::cout << a << std::endl;
+}
+
+// ------------
+
+// This example sees a class containing 'static' members and using access specifiers to
+// restrict access to those members. Since all the members are static, this class is not
+// meant to be instantiated [i.e. objects created from it - it can be done, but they would
+// all be the exact same object :)], but merely uses the 'class' facility to encapsulate
+// [i.e. group together] and allow selective access [i.e. hide some parts, allow access to
+// others]. For Design Pattern afficiandos, this is a crude example of the Singleton Pattern
+
+// File: 'counter.h'
+class Counter
+{
+public:
+ static int increment();
+ static int decrement();
+private:
+ static int counter;
+};
+
+// ----
+
+// File: 'counter.cpp'
+#include "counter.h"
+
+int Counter::increment() { return ++counter; }
+int Counter::decrement() { return --counter; }
+
+int Counter::counter = 0;
+
+// ----
+
+// File: 'test.cpp'
+#include <iostream>
+#include "counter.h"
+
+int main()
+{
+ int a = Counter::increment();
+ std::cout << a << std::endl;
+
+ a = Counter::decrement();
+ std::cout << a << std::endl;
+}
+
+// @@PLEAC@@_10.4
+// Standard C++ offers no facility for performing adhoc, runtime stack inspection; therefore,
+// information such as the currently-executing function name, cannot be obtained. Now, this
+// isn't to say that such facilities don't exist [since, after all, a symbolic debugger works
+// by doing just this - stack inspection, among other things], but that such features are, for
+// native code compiled languages like C++, 'extra-language' and development tool-specific
+
+// @@PLEAC@@_10.5
+// Standard C++ supports both
+// * 'pass-by-value': a copy of an argument is passed when calling a function; in this way
+// the original is safe from modification, but a copying overhead is incurred which may
+// adversely affect performance
+// * 'pass-by-reference': the address of an argument is passed when calling a function;
+// allows the original to be modified, and incurrs no performance penalty from copying
+//
+// The 'pass-by-value' mechanism works in the same way as in the Standard C language [see
+// corresponding section in PLEAC-C/Posix/GNU]. The 'pass-by-reference' mechanism provides
+// the same functionality as passing a pointer-to-a-pointer-to-an-argument, but without the
+// complications arising from having to correctly dereference. Using a reference to a non-const
+// item allows:
+// * The item's state to be modified i.e. if an object was passed, it can be mutated [effect
+// can be mimiced by passing a pointer to the item]
+// * The item, itself, can be replaced with a new item i.e. the memory location to which the
+// reference refers is updated [effect can be mimiced by passing a pointer-to-a-pointer to
+// the item]
+
+#include <cstddef>
+#include <vector>
+
+// 'pass-by-value': a copy of each vector is passed as an argument
+// void array_diff(const std::vector<int> arr1, const std::vector<int> arr2);
+
+// 'pass-by-reference': the address of each vector is passed as an argument. Some variants:
+// * Disallow both vector replacement and alteration of its contents
+// void array_diff(const std::vector<const int>& arr1, const std::vector<const int>& arr2);
+// * Disallow vector replacement only
+// void array_diff(const std::vector<int>& arr1, const std::vector<int>& arr2);
+// * Disallow alteration of vector contents only
+// void array_diff(std::vector<const int>& arr1, std::vector<const int>& arr2);
+// * Allow replacement / alteration
+// void array_diff(std::vector<int>& arr1, std::vector<int>& arr2);
+
+void array_diff(const std::vector<int>& arr1, const std::vector<int>& arr2);
+
+// ----
+
+int main()
+{
+ // Load vectors from built-in arrays, or use Boost 'assign' library
+ const int arr1[] = {1, 2, 3}, arr2[] = {4, 5, 6};
+ const size_t arrsize = 3;
+
+ // Function call is the same whether 'array_diff' is declared to be 'pass-by-value'
+ // or 'pass-by-reference'
+ array_diff(std::vector<int>(arr1, arr1 + arrsize), std::vector<int>(arr2, arr2 + arrsize));
+}
+
+// ----
+
+// void array_diff(const std::vector<int> arr1, const std::vector<int> arr2)
+// {
+// ; // 'arr1' and 'arr2' are copies of the originals
+// }
+
+void array_diff(const std::vector<int>& arr1, const std::vector<int>& arr2)
+{
+ ; // 'arr1' and 'arr2' are references to the originals
+}
+
+// ----------------------------
+
+#include <cstddef>
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+std::vector<int> add_vecpair(const std::vector<int>& arr1, const std::vector<int>& arr2);
+
+// ----
+
+int main()
+{
+ // Load vectors from built-in arrays, or use Boost 'assign' library
+ const int aa[] = {1, 2}, ba[] = {5, 8};
+ size_t arrsize = 2;
+
+ const std::vector<int> a(aa, aa + arrsize), b(ba, ba + arrsize);
+
+ std::vector<int> c = add_vecpair(a, b);
+}
+
+// ----
+
+std::vector<int> add_vecpair(const std::vector<int>& arr1, const std::vector<int>& arr2)
+{
+ std::vector<int> retvec; retvec.reserve(arr1.size());
+ std::transform(arr1.begin(), arr1.end(), arr2.begin(), back_inserter(retvec), std::plus<int>());
+ return retvec;
+}
+
+// @@PLEAC@@_10.6
+// Please refer to the corresponding section in PLEAC-C/Posix/GNU since the points raised there
+// apply to C++ also. Examples here don't so much illustrate C++'s handling of 'return context'
+// as much as how disparate types might be handled in a reasonably uniform manner
+
+// Here, 'mysub' is implemented as a function template, and its return type varies with the
+// argument type. In most cases the compiler is able to infer the return type from the
+// argument, however, it is possible to pass the type as a template parameter. Note this
+// code operates at compile-time, as does any template-only code
+
+#include <cstddef>
+
+#include <string>
+#include <vector>
+
+template <typename T> T mysub(const T& t) { return t; }
+
+// ----
+
+int main()
+{
+ // 1. Type information inferred by compiler
+ int i = mysub(5);
+
+ double d = mysub(7.6);
+
+ const int arr[] = {1, 2, 3};
+ const size_t arrsize = sizeof(arr) / sizeof(arr[0]);
+
+ std::vector<int> v = mysub(std::vector<int>(arr, arr + arrsize));
+
+ // 2. Type information provided by user
+ // Pass a 'const char*' argument and specify type information in the call
+ std::string s = mysub<std::string>("xyz");
+
+ // Could avoid specifying type information by passing a 'std::string' argument
+ // std::string s = mysub(std::string("xyz"));
+}
+
+// ----------------------------
+
+// This is a variant on the previous example that uses the Boost Library's 'any' type as a
+// generic 'stub' type
+
+#include <string>
+#include <vector>
+
+#include <boost/any.hpp>
+
+template <typename T> boost::any mysub(const T& t) { return boost::any(t); }
+
+// ----
+
+int main()
+{
+ std::vector<boost::any> any;
+
+ // Add various types [encapsulated in 'any' objects] to the container
+ any.push_back(mysub(5));
+ any.push_back(mysub(7.6));
+ any.push_back(mysub(std::vector<int>(5, 5)));
+ any.push_back(mysub(std::string("xyz")));
+
+ // Extract the various types from the container by appropriately casting the relevant
+ // 'any' object
+ int i = boost::any_cast<int>(any[0]);
+ double d = boost::any_cast<double>(any[1]);
+ std::vector<int> v = boost::any_cast< std::vector<int> >(any[2]);
+ std::string s = boost::any_cast<std::string>(any[3]);
+}
+
+// @@PLEAC@@_10.7
+// Just like the C language, C++ offers no support for named / keyword parameters. It is of
+// course possible to mimic such functionality the same way it is done in C [see corresponding
+// section in PLEAC-C/Posix/GNU], the most obvious means being by passing a set of key/value
+// pairs in a std::map. This will not be shown here. Instead, two quite C++-specific examples
+// will be provided, based on:
+//
+// * Named Parameter Idiom [see: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18]
+// * Boost 'parameter' Library [see: http://www.boost.org/libs/parameter/doc/html/index.html]
+
+#include <iostream>
+#include <map>
+
+class TimeEntry
+{
+public:
+ explicit TimeEntry(int value = 0, char dim = 's');
+
+ bool operator<(const TimeEntry& right) const;
+
+ friend std::ostream& operator<<(std::ostream& out, const TimeEntry& t);
+
+private:
+ int value_;
+ char dim_;
+};
+
+typedef std::pair<const int, TimeEntry> TENTRY;
+typedef std::map<const int, TimeEntry> TIMETBL;
+
+class RaceTime
+{
+public:
+ const static int START_TIME, FINISH_TIME, INCR_TIME;
+
+public:
+ explicit RaceTime();
+
+ RaceTime& start_time(const TimeEntry& time);
+ RaceTime& finish_time(const TimeEntry& time);
+ RaceTime& incr_time(const TimeEntry& time);
+
+ friend std::ostream& operator<<(std::ostream& out, const RaceTime& r);
+
+private:
+ TIMETBL timetbl_;
+};
+
+const int RaceTime::START_TIME = 0, RaceTime::FINISH_TIME = 1, RaceTime::INCR_TIME = 2;
+
+void the_func(const RaceTime& r);
+
+// ----
+
+int main()
+{
+ the_func(RaceTime().start_time(TimeEntry(20, 's')).finish_time(TimeEntry(5, 'm')).incr_time(TimeEntry(5, 's')));
+
+ the_func(RaceTime().start_time(TimeEntry(5, 'm')).finish_time(TimeEntry(30, 'm')));
+
+ the_func(RaceTime().start_time(TimeEntry(30, 'm')));
+}
+
+// ----
+
+std::ostream& operator<<(std::ostream& out, const TimeEntry& t)
+{
+ out << t.value_ << t.dim_; return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const RaceTime& r)
+{
+ RaceTime& r_ = const_cast<RaceTime&>(r);
+
+ out << "start_time: " << r_.timetbl_[RaceTime::START_TIME]
+ << "\nfinish_time: " << r_.timetbl_[RaceTime::FINISH_TIME]
+ << "\nincr_time: " << r_.timetbl_[RaceTime::INCR_TIME];
+
+ return out;
+}
+
+TimeEntry::TimeEntry(int value, char dim) : value_(value), dim_(dim) {}
+
+bool TimeEntry::operator<(const TimeEntry& right) const
+{
+ return (dim_ == right.dim_) ? (value_ < right.value_) : !(dim_ < right.dim_);
+}
+
+RaceTime::RaceTime()
+{
+ timetbl_.insert(TENTRY(START_TIME, TimeEntry(0, 's')));
+ timetbl_.insert(TENTRY(FINISH_TIME, TimeEntry(0, 's')));
+ timetbl_.insert(TENTRY(INCR_TIME, TimeEntry(0, 's')));
+}
+
+RaceTime& RaceTime::start_time(const TimeEntry& time)
+{
+ timetbl_[START_TIME] = time; return *this;
+}
+
+RaceTime& RaceTime::finish_time(const TimeEntry& time)
+{
+ timetbl_[FINISH_TIME] = time; return *this;
+}
+
+RaceTime& RaceTime::incr_time(const TimeEntry& time)
+{
+ timetbl_[INCR_TIME] = time; return *this;
+}
+
+void the_func(const RaceTime& r)
+{
+ std::cout << r << std::endl;
+}
+
+// ----------------------------
+
+// The Boost 'parameter' library requires a significant amount of setup code to be written,
+// much more than this section warrants. My recommendation is to read carefully through the
+// tutorial to determine whether a problem for which it is being considered justifies all
+// the setup.
+
+// @@PLEAC@@_10.8
+// The Boost 'tuple' Library also allows multiple assignment to variables, including the
+// selective skipping of return values
+
+#include <iostream>
+
+#include <boost/tuple/tuple.hpp>
+
+typedef boost::tuple<int, int, int> T3;
+
+T3 func();
+
+// ----
+
+int main()
+{
+ int a = 6, b = 7, c = 8;
+ std::cout << a << ',' << b << ',' << c << std::endl;
+
+ // A tuple of references to the referred variables is created; the values
+ // captured from the returned tuple are thus multiply-assigned to them
+ boost::tie(a, b, c) = func();
+ std::cout << a << ',' << b << ',' << c << std::endl;
+
+ // Variables can still be individually referenced
+ a = 11; b = 23; c = 56;
+ std::cout << a << ',' << b << ',' << c << std::endl;
+
+ // Return values may be ignored; affected variables retain existing values
+ boost::tie(a, boost::tuples::ignore, c) = func();
+ std::cout << a << ',' << b << ',' << c << std::endl;
+}
+
+// ----
+
+T3 func() { return T3(3, 6, 9); }
+
+// @@PLEAC@@_10.9
+// Like Standard C, C++ allows only the return of a single value. The return of multiple values
+// *can*, however, be simulated by packaging them within an aggregate type [as in C], or a
+// custom class, or one of the STL containers like std::vector. Probably the most robust, and
+// [pseudo]-standardised, approach is to use the Boost 'tuple' Library, as will be done in this
+// section. Notes:
+// * Use made of Boost 'assign' Library to simplify container loading; this is a *very* handy
+// library
+// * Use made of Boost 'any' Library to make containers heterogenous; 'variant' Library is
+// similar, and is more appropriate where type-safe container traversal is envisaged e.g.
+// for printing
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <boost/any.hpp>
+#include <boost/tuple/tuple.hpp>
+
+#include <boost/assign/std/vector.hpp>
+#include <boost/assign/list_inserter.hpp>
+
+typedef std::vector<boost::any> ARRAY;
+typedef std::map<std::string, boost::any> HASH;
+typedef boost::tuple<ARRAY, HASH> ARRAY_HASH;
+
+ARRAY_HASH some_func(const ARRAY& array, const HASH& hash);
+
+// ----
+
+int main()
+{
+ // Load containers using Boost 'assign' Library
+ using namespace boost::assign;
+ ARRAY array; array += 1, 2, 3, 4, 5;
+ HASH hash; insert(hash) ("k1", 1) ("k2", 2) ("k3", 3);
+
+ // Pass arguments to 'somefunc' and retrieve them as members of a tuple
+ ARRAY_HASH refs = some_func(array, hash);
+
+ // Retrieve copy of 'array' from tuple
+ ARRAY ret_array = boost::get<0>(refs);
+
+ // Retrieve copy of 'hash' from tuple
+ HASH ret_hash = boost::get<1>(refs);
+}
+
+// ----
+
+ARRAY_HASH some_func(const ARRAY& array, const HASH& hash)
+{
+ ; // ... do something with 'array' and 'hash'
+
+ return ARRAY_HASH(array, hash);
+}
+
+// @@PLEAC@@_10.10
+// Like function calls in Standard C, function calls in C++ need to conform to signature
+// requirements; a function call must match its declaration with the same number, and type,
+// of arguments passed [includes implicitly-passed default arguments], and the same return
+// value type. Thus, unlike Perl, a function declared to return a value *must* do so, thus
+// cannot 'return nothing' to indicate failure.
+// Whilst in Standard C certain conventions like returning NULL pointers, or returning -1, to
+// indicate the 'failure' of a task [i.e. function return codes are checked, and control
+// proceeds conditionally] are used, Standard C++ sports facilities which lessen the need for
+// dong the same. Specifically, C++ offers:
+// * Built-in exception handling which can be used to detect [and perhaps recover from],
+// all manner of unusual, or erroneous / problematic situations. One recommended use is
+// to avoid writing code that performs a lot of return code checking
+// * Native OOP support allows use of the Null Object Design Pattern. Put simply, rather than
+// than checking return codes then deciding on an action, an object with some predefined
+// default behaviour is returned / used where an unusual / erroneous / problematic situation
+// is encountered. This approach could be as simple as having some sort of default base
+// class member function behaviour, or as complex as having a diagnostic-laden object created
+// * Functions can still return 'error-indicating entities', but rather than primitive types
+// like 'int's or NULL pointers, complex objects can be returned. For example, the Boost
+// Library sports a number of such types:
+// - 'tuple'
+// - 'any', 'variant' and 'optional'
+// - 'tribool' [true, false, indeterminate]
+
+// Exception Handling Example
+
+class XYZ_exception {};
+
+int func();
+
+// ----
+
+int main()
+{
+ int valid_value = 0;
+
+ try
+ {
+ ; // ...
+
+ valid_value = func();
+
+ ; // ...
+ }
+
+ catch(const XYZ_exception& e)
+ {
+ ; // ...
+ }
+}
+
+// ----
+
+int func()
+{
+ bool error_detected = false;
+ int valid_value;
+
+ ; // ...
+
+ if (error_detected) throw XYZ_exception();
+
+ ; // ...
+
+ return valid_value;
+}
+
+// ------------
+
+// Null Object Design Pattern Example
+
+#include <iostream>
+
+class Value
+{
+public:
+ virtual void do_something() = 0;
+};
+
+class NullValue : public Value
+{
+public:
+ virtual void do_something();
+};
+
+class ValidValue : public Value
+{
+public:
+ virtual void do_something();
+};
+
+Value* func();
+
+// ----
+
+int main()
+{
+ // Error checking is performed within 'func'. However, regardless of the outcome, an
+ // object of 'Value' type is returned which possesses similar behaviour, though appropriate
+ // to whether processing was successful or not. In this way no error checking is needed
+ // outside of 'func'
+ Value* v = func();
+
+ v->do_something();
+
+ delete v;
+}
+
+// ----
+
+void NullValue::do_something()
+{
+ std::cout << "*null*" << std::endl;
+}
+
+void ValidValue::do_something()
+{
+ std::cout << "valid" << std::endl;
+}
+
+Value* func()
+{
+ bool error_detected = true;
+
+ ; // ...
+
+ if (error_detected) return new NullValue;
+
+ ; // ...
+
+ return new ValidValue;
+}
+
+// ----------------------------
+
+// The Boost 'optional' library has many uses, but in the current context, one is of particular
+// use: returning a specified type [thus satisfying language requirements], but whose value
+// may be 'set' [if the function succeeded] or 'unset' [if it failed], and this condition very
+// easily checked
+
+#include <iostream>
+
+#include <cstdlib>
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <boost/optional/optional.hpp>
+
+class func_fail
+{
+public:
+ explicit func_fail(const std::string& msg) : msg_(msg) {}
+ const std::string& msg() const { return msg_; }
+private:
+ const std::string msg_;
+};
+
+// ----
+
+void die(const std::string& msg);
+
+boost::optional<int> sfunc();
+boost::optional< std::vector<int> > afunc();
+boost::optional< std::map<std::string, int> > hfunc();
+
+// ------------
+
+int main()
+{
+ try
+ {
+ boost::optional<int> s;
+ boost::optional< std::vector<int> > a;
+ boost::optional< std::map<std::string, int> > h;
+
+ if (!(s = sfunc())) throw func_fail("'sfunc' failed");
+ if (!(a = afunc())) throw func_fail("'afunc' failed");
+ if (!(h = hfunc())) throw func_fail("'hfunc' failed");
+
+ ; // ... do stuff with 's', 'a', and 'h' ...
+ int scalar = *s;
+
+ ; // ...
+ }
+
+ catch (const func_fail& e)
+ {
+ die(e.msg());
+ }
+
+ ; // ... other code executed if no error above ...
+}
+
+// ------------
+
+void die(const std::string& msg)
+{
+ std::cerr << msg << std::endl;
+
+ // Should only be used if all objects in the originating local scope have been destroyed
+ std::exit(EXIT_FAILURE);
+}
+
+// ----
+
+boost::optional<int> sfunc()
+{
+ bool error_detected = true;
+
+ int valid_int_value;
+
+ ; // ...
+
+ if (error_detected) return boost::optional<int>();
+
+ ; // ...
+
+ return boost::optional<int>(valid_int_value);
+}
+
+boost::optional< std::vector<int> > afunc()
+{
+ // ... code not shown ...
+
+ return boost::optional< std::vector<int> >();
+
+ // ... code not shown
+}
+
+boost::optional< std::map<std::string, int> > hfunc()
+{
+ // ... code not shown ...
+
+ return boost::optional< std::map<std::string, int> >();
+
+ // ... code not shown ...
+}
+
+// @@PLEAC@@_10.11
+// Whilst in Perl function prototyping is optional, this is not the case in C++, where it is
+// necessary to:
+// * Declare a function before use; this could either be a function declaration separate from
+// the function definition, or the function definition itself which serves as its own
+// declaration
+// * Specify both parameter positional and type information; parameter names are optional in
+// declarations, mandatory in definitions
+// * Specify return type
+
+#include <iostream>
+#include <vector>
+
+// Function Declaration
+std::vector<int> myfunc(int arg1, int arg2); // Also possible: std::vector<int> myfunc(int, int);
+
+// ----
+
+int main()
+{
+ // Call function with all required arguments; this is the only calling method
+ // [except for calling via function pointer which still needs all arguments supplied]
+ std::vector<int> results = myfunc(3, 5);
+
+ // Let's look at our return array's contents
+ std::cout << results[0] << ':' << results[1] << std::endl;
+}
+
+// ----
+
+// Function Definition
+std::vector<int> myfunc(int arg1, int arg2)
+{
+ std::vector<int> r;
+
+ std::back_inserter(r) = arg1;
+ std::back_inserter(r) = arg2;
+
+ return r;
+}
+
+// ------------
+
+// A version on the above code that is generic, that is, making use of the C++ template
+// mechanism to work with any type
+
+#include <iostream>
+#include <vector>
+
+// Function Declaration
+template <class T> std::vector<T> myfunc(const T& arg1, const T& arg2);
+
+// ----
+
+int main()
+{
+ std::vector<int> results = myfunc(3, 5);
+
+ std::cout << results[0] << ':' << results[1] << std::endl;
+}
+
+// ----
+
+// Function Definition
+template <class T> std::vector<T> myfunc(const T& arg1, const T& arg2)
+{
+ std::vector<T> r;
+
+ std::back_inserter(r) = arg1;
+ std::back_inserter(r) = arg2;
+
+ return r;
+}
+
+// ------------
+
+// Other Perl examples are omitted since there is no variation in C++ function calling or
+// parameter handling
+
+// @@PLEAC@@_10.12
+// One of the key, non-object oriented features of Standard C++ is its built-in support for
+// exceptions / exception handling. The feature is well-integrated into the language, including
+// a set of predefined exception classes included in, and used by, the Standard Library, is
+// quite easy to use, and helps the programmer write robust code provided certain conventions
+// are followed. On the downside, the C++ exception handling system is criticised for imposing
+// significant runtime overhead, as well as increasing executable code size [though this
+// varies considerably between CPU's, OS's, and compilers]. Please refer to the corresponding
+// section in PLEAC-C/Posix/GNU for pertinent reading references.
+//
+// The example code below matches the PLEAC-C/Posix/GNU example rather than the Perl code. Note:
+// * A very minimal, custom exception class is implemented; a more complex class, one richer in
+// diagnostic information, could have been implemented, or perhaps one based on a standard
+// exception class like 'std::exception'
+// * Ordinarily error / exception messages are directed to 'std::cerr' or 'std::clog'
+// * General recommendation is to throw 'temporaries' [via invoking a constructor],
+// and to 'catch' as const reference(s)
+// * Proper 'cleanup' is very important; consult a suitable book for guidance on writing
+// 'exception safe' code
+
+#include <iostream>
+#include <string>
+
+class FullmoonException
+{
+public:
+ explicit FullmoonException(const std::string& msg) : msg_(msg) {}
+
+ friend std::ostream& operator<<(std::ostream& out, const FullmoonException& e)
+ {
+ out << e.msg_; return out;
+ }
+private:
+ const std::string msg_;
+};
+
+// ----
+
+int main()
+{
+ std::cout << "main - entry" << std::endl;
+
+ try
+ {
+ std::cout << "try block - entry" << std::endl;
+ std::cout << "... doing stuff ..." << std::endl;
+
+ // if (... error condition detected ...)
+ throw FullmoonException("... the problem description ...");
+
+ // Control never gets here ...
+ std::cout << "try block - end" << std::endl;
+ }
+
+ catch(const FullmoonException& e)
+ {
+ std::cout << "Caught a'Fullmoon' exception. Message: "
+ << "[" << e << "]"
+ << std::endl;
+ }
+
+ catch(...)
+ {
+ std::cout << "Caught an unknown exceptione" << std::endl;
+ }
+
+ // Control gets here regardless of whether an exception is thrown or not
+ std::cout << "main - end" << std::endl;
+}
+
+// @@PLEAC@@_10.13
+// Standard C++ sports a namespace facility which allows an application to be divided into
+// logical sub-systems, each of which operates within its own scope. Put very simply, the same
+// identifiers [i.e. name of types, objects, and functions] may be each used in a namespace
+// without fear of a nameclash occurring when logical sub-systems are variously combined as
+// an application. The name-clash problem is inherent in single-namespace languages like C; it
+// often occurs when several third-party libraries are used [a common occurrence in C], or
+// when an application scales up. The remedy is to rename identifiers, or, in the case of
+// functions that cannot be renamed, to wrap them up in other functions in a separate source
+// file. Of course the problem may be minimised via strict adherence to naming conventions.
+//
+// The C++ namespace facility is important, too, because it avoids the need to utilise certain
+// C language practices, in particular:
+// * Use of, possibly, 'clumsy' naming conventions [as described above]
+// * Partition an application by separating logically-related items into separate source
+// files. Namespaces cross file boundaries, so items may reside in several source files
+// and still comprise a single, logical sub-system
+// * Anonymous namespaces avoid use of the 'static' keyword in creating file scope globals
+
+// Global variable
+int age = 18;
+
+// ----
+
+void print_age()
+{
+ // Global value, 'age', is accessed
+ std::cout << "Age is " << age << std::endl;
+}
+
+// ------------
+
+int main()
+{
+ // A local variable named, 'age' will act to 'shadow' the globally
+ // defined version, thus any changes to, 'age', will not affect
+ // the global version
+ int age = 5;
+
+ // Prints 18, the current value of the global version
+ print_age();
+
+ // Local version is altered, *not* global version
+ age = 23;
+
+ // Prints 18, the current value of the global version
+ print_age();
+}
+
+// ----------------------------
+
+// Global variable
+int age = 18;
+
+// ----
+
+void print_age()
+{
+ // Global value, 'age', is accessed
+ std::cout << "Age is " << age << std::endl;
+}
+
+// ------------
+
+int main()
+{
+ // Here no local version declared: any changes affect global version
+ age = 5;
+
+ // Prints 5, the new value of the global version
+ print_age();
+
+ // Global version again altered
+ age = 23;
+
+ // Prints 23, the new value of the global version
+ print_age();
+}
+
+// ----------------------------
+
+// Global variable
+int age = 18;
+
+// ----
+
+void print_age()
+{
+ // Global value, 'age', is accessed
+ std::cout << "Age is " << age << std::endl;
+}
+
+// ------------
+
+int main()
+{
+ // Global version value saved into local version
+ int age = ::age;
+
+ // Prints 18, the new value of the global version
+ print_age();
+
+ // Global version this time altered
+ ::age = 23;
+
+ // Prints 23, the new value of the global version
+ print_age();
+
+ // Global version value restored from saved local version
+ ::age = age;
+
+ // Prints 18, the restored value of the global version
+ print_age();
+}
+
+// @@PLEAC@@_10.14
+// Please refer to the corresponding section in PLEAC-C/Posix/GNU since the points raised there
+// about functions and function pointers apply equally to Standard C++ [briefly: functions
+// cannot be redefined; several same-signature functions may be called via the same function
+// pointer variable; code cannot be generated 'on-the-fly' (well, not without the use of
+// several external tools, making it an extra-language, not integral, feature)].
+// @@INCOMPLETE@@
+
+// @@PLEAC@@_10.15
+// Please refer to the corresponding section in PLEAC-C/Posix/GNU since all the points raised
+// there apply equally to Standard C++ [briefly: undefined function calls are compiler-detected
+// errors; function-pointer-based calls can't be checked for integrity].
+// @@INCOMPLETE@@
+
+// @@PLEAC@@_10.16
+// Standard C++ does not support either simple nested functions or closures, therefore the
+// example cannot be implemented exactly as per the Perl code
+
+/* ===
+int outer(int arg)
+{
+ int x = arg + 35;
+
+ // *** wrong - illegal C++ ***
+ int inner() { return x * 19; }
+
+ return x + inner();
+}
+=== */
+
+// The problem may, of course, be solved by defining two functions using parameter passing
+// where appropriate, but this is contrary to the intent of the original Perl code
+int inner(int x)
+{
+ return x * 19;
+}
+
+int outer(int arg)
+{
+ int x = arg + 35;
+ return x + inner(x);
+}
+
+// An arguably better [but far more complicated] approach is to encapsulate all items within
+// a namespace, but again, is an approach that is counter the intent of the original Perl code
+#include <iostream>
+
+namespace nst
+{
+ int x;
+ int inner();
+ int outer(int arg);
+}
+
+// ----
+
+int main()
+{
+ std::cout << nst::outer(3) << std::endl;
+}
+
+// ----
+
+int nst::inner()
+{
+ return nst::x * 19;
+}
+
+int nst::outer(int arg)
+{
+ nst::x = arg + 35;
+ return nst::x + nst::inner();
+}
+
+// Another way to solve this problem and avoiding the use of an external function, is to
+// create a local type and instantiate an object passing any required environment context
+// to the constructor. Then, what appears as a parameterless nested function call, can be
+// effected using 'operator()'. This approach most closely matches the original Perl code
+
+int outer(int arg)
+{
+ int x = arg + 35;
+
+ // 'Inner' is what is known as a Functor or Function Object [or Command Design Pattern]; it
+ // allows objects that capture state / context to be instantiated, and that state / context
+ // used / retained / altered at multiple future times. Both the STL and Boost Libraries
+ // provide extensive support these constructs
+ struct Inner
+ {
+ int n_;
+ explicit Inner(int n) : n_(n) {}
+ int operator()() const { return n_ * 19; }
+ } inner(x);
+
+ return x + inner();
+}
+
+// @@PLEAC@@_10.17
+// @@INCOMPLETE@@
+// @@INCOMPLETE@@
+
diff --git a/test/scanners/cpp/suite.rb b/test/scanners/cpp/suite.rb new file mode 100644 index 0000000..7389d40 --- /dev/null +++ b/test/scanners/cpp/suite.rb @@ -0,0 +1,2 @@ +class Cpp < CodeRay::TestCase +end diff --git a/test/scanners/cpp/wedekind.expected.raydebug b/test/scanners/cpp/wedekind.expected.raydebug new file mode 100644 index 0000000..e85226f --- /dev/null +++ b/test/scanners/cpp/wedekind.expected.raydebug @@ -0,0 +1,12 @@ +reserved(template)operator(<) reserved(typename) ident(T) operator(>) +ident(T) ident(plus)operator(() directive(const) operator(&)ident(T) ident(x) operator(\)) +operator({) + reserved(return) operator(-)ident(x)operator(;) +operator(}) + +reserved(template)operator(<) reserved(typename) ident(T) operator(>) +reserved(class) class(image) +operator({) +directive(public)operator(:) + ident(image)operator(() directive(const) ident(image)operator(<) ident(T) operator(>) operator(&)ident(_image) operator(\)) operator({}) +operator(};) diff --git a/test/scanners/cpp/wedekind.in.cpp b/test/scanners/cpp/wedekind.in.cpp new file mode 100644 index 0000000..68b0dba --- /dev/null +++ b/test/scanners/cpp/wedekind.in.cpp @@ -0,0 +1,12 @@ +template< typename T > +T plus( const &T x ) +{ + return -x; +} + +template< typename T > +class image +{ +public: + image( const image< T > &_image ) {} +}; |